Cleaned up the code

This commit is contained in:
Frederik Jacobsen 2025-10-18 22:28:24 +02:00
parent 5f1d7ef530
commit f8fb0a431f
43 changed files with 1132 additions and 1187 deletions

View File

@ -5,8 +5,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\XSDVisualiser\Models\**\*.cs" Link="Models\%(RecursiveDir)%(Filename)%(Extension)" /> <Compile Include="..\XSDVisualiser\Models\**\*.cs" Link="Models\%(RecursiveDir)%(Filename)%(Extension)"/>
<Compile Include="..\XSDVisualiser\Parsing\**\*.cs" Link="Parsing\%(RecursiveDir)%(Filename)%(Extension)" /> <Compile Include="..\XSDVisualiser\Parsing\**\*.cs" Link="Parsing\%(RecursiveDir)%(Filename)%(Extension)"/>
<Compile Include="..\XSDVisualiser\Utils\**\*.cs" Link="Utils\%(RecursiveDir)%(Filename)%(Extension)" /> <Compile Include="..\XSDVisualiser\Utils\**\*.cs" Link="Utils\%(RecursiveDir)%(Filename)%(Extension)"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -6,30 +6,30 @@
RequestedThemeVariant="Light"> RequestedThemeVariant="Light">
<Application.Resources> <Application.Resources>
<!-- App-level resource brushes for consistent, accessible colors --> <!-- App-level resource brushes for consistent, accessible colors -->
<SolidColorBrush x:Key="AccentBrush" Color="#2563EB"/> <SolidColorBrush x:Key="AccentBrush" Color="#2563EB" />
<SolidColorBrush x:Key="AccentBrushLight" Color="#DBEAFE"/> <SolidColorBrush x:Key="AccentBrushLight" Color="#DBEAFE" />
<SolidColorBrush x:Key="PanelBackgroundBrush" Color="#FFFFFFFF"/> <SolidColorBrush x:Key="PanelBackgroundBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="PanelBorderBrush" Color="#E5E7EB"/> <SolidColorBrush x:Key="PanelBorderBrush" Color="#E5E7EB" />
<SolidColorBrush x:Key="SubtleTextBrush" Color="#475569"/> <SolidColorBrush x:Key="SubtleTextBrush" Color="#475569" />
<SolidColorBrush x:Key="MutedTextBrush" Color="#64748B"/> <SolidColorBrush x:Key="MutedTextBrush" Color="#64748B" />
<SolidColorBrush x:Key="SeparatorBrush" Color="#E2E8F0"/> <SolidColorBrush x:Key="SeparatorBrush" Color="#E2E8F0" />
<SolidColorBrush x:Key="BadgeBackgroundBrush" Color="#F1F5F9"/> <SolidColorBrush x:Key="BadgeBackgroundBrush" Color="#F1F5F9" />
<!-- Global converters --> <!-- Global converters -->
<conv:CardinalityToLabelConverter x:Key="CardinalityToLabel"/> <conv:CardinalityToLabelConverter x:Key="CardinalityToLabel" />
<conv:OptionalToBorderBrushConverter x:Key="OptionalToBrush"/> <conv:OptionalToBorderBrushConverter x:Key="OptionalToBrush" />
<conv:OptionalToThicknessConverter x:Key="OptionalToThickness"/> <conv:OptionalToThicknessConverter x:Key="OptionalToThickness" />
<conv:CollectionHasItemsConverter x:Key="HasItems"/> <conv:CollectionHasItemsConverter x:Key="HasItems" />
</Application.Resources> </Application.Resources>
<Application.Styles> <Application.Styles>
<!-- Use Fluent theme for consistent UI controls --> <!-- Use Fluent theme for consistent UI controls -->
<fluent:FluentTheme/> <fluent:FluentTheme />
<!-- Default TextBlock accessibility improvements --> <!-- Default TextBlock accessibility improvements -->
<Style Selector="TextBlock"> <Style Selector="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/> <Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="TextTrimming" Value="CharacterEllipsis"/> <Setter Property="TextTrimming" Value="CharacterEllipsis" />
</Style> </Style>
</Application.Styles> </Application.Styles>
</Application> </Application>

View File

@ -2,10 +2,10 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace XSDVisualiser.Desktop namespace XSDVisualiser.Desktop;
public class App : Application
{ {
public partial class App : Application
{
public override void Initialize() public override void Initialize()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
@ -14,10 +14,7 @@ namespace XSDVisualiser.Desktop
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()
{ {
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow(); desktop.MainWindow = new MainWindow();
}
base.OnFrameworkInitializationCompleted(); base.OnFrameworkInitializationCompleted();
} }
}
} }

View File

@ -1,12 +1,11 @@
using System;
using System.Globalization; using System.Globalization;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using XSDVisualiser.Models; using XSDVisualiser.Core;
namespace XSDVisualiser.Desktop.Converters namespace XSDVisualiser.Desktop.Converters;
public class CardinalityToLabelConverter : IValueConverter
{ {
public class CardinalityToLabelConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is not Occurs occ) return null; if (value is not Occurs occ) return null;
@ -17,6 +16,7 @@ namespace XSDVisualiser.Desktop.Converters
} }
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotSupportedException(); {
throw new NotSupportedException();
} }
} }

View File

@ -1,16 +1,15 @@
using System;
using System.Collections; using System.Collections;
using System.Globalization; using System.Globalization;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
namespace XSDVisualiser.Desktop.Converters namespace XSDVisualiser.Desktop.Converters;
/// <summary>
/// Returns true if the bound value is a collection with at least one item. Supports IEnumerable and ICollection.
/// Optional parameter "Invert" to invert the boolean result.
/// </summary>
public sealed class CollectionHasItemsConverter : IValueConverter
{ {
/// <summary>
/// Returns true if the bound value is a collection with at least one item. Supports IEnumerable and ICollection.
/// Optional parameter "Invert" to invert the boolean result.
/// </summary>
public sealed class CollectionHasItemsConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
var hasItems = false; var hasItems = false;
@ -32,6 +31,7 @@ namespace XSDVisualiser.Desktop.Converters
} }
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotSupportedException(); {
throw new NotSupportedException();
} }
} }

View File

@ -1,27 +1,25 @@
using System;
using System.Globalization; using System.Globalization;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Media; using Avalonia.Media;
using XSDVisualiser.Models; using XSDVisualiser.Core;
namespace XSDVisualiser.Desktop.Converters namespace XSDVisualiser.Desktop.Converters;
public class OptionalToBorderBrushConverter : IValueConverter
{ {
public class OptionalToBorderBrushConverter : IValueConverter
{
private static readonly IBrush RequiredBrush = new SolidColorBrush(Color.FromRgb(0x33, 0x99, 0x33)); private static readonly IBrush RequiredBrush = new SolidColorBrush(Color.FromRgb(0x33, 0x99, 0x33));
private static readonly IBrush OptionalBrush = new SolidColorBrush(Color.FromRgb(0xCC, 0x66, 0x33)); private static readonly IBrush OptionalBrush = new SolidColorBrush(Color.FromRgb(0xCC, 0x66, 0x33));
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Occurs occ) if (value is Occurs occ)
{
// Optional if minOccurs == 0 // Optional if minOccurs == 0
return occ.Min == 0 ? OptionalBrush : RequiredBrush; return occ.Min == 0 ? OptionalBrush : RequiredBrush;
}
return RequiredBrush; return RequiredBrush;
} }
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotSupportedException(); {
throw new NotSupportedException();
} }
} }

View File

@ -1,27 +1,25 @@
using System;
using System.Globalization; using System.Globalization;
using Avalonia; using Avalonia;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using XSDVisualiser.Models; using XSDVisualiser.Core;
namespace XSDVisualiser.Desktop.Converters namespace XSDVisualiser.Desktop.Converters;
public class OptionalToThicknessConverter : IValueConverter
{ {
public class OptionalToThicknessConverter : IValueConverter private static readonly Thickness Required = new(2);
{ private static readonly Thickness Optional = new(2);
private static readonly Thickness Required = new Thickness(2);
private static readonly Thickness Optional = new Thickness(2);
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Occurs occ) if (value is Occurs occ)
{
// We could vary thickness; for now same thickness regardless, reserved for future styling. // We could vary thickness; for now same thickness regardless, reserved for future styling.
return occ.Min == 0 ? Optional : Required; return occ.Min == 0 ? Optional : Required;
}
return Required; return Required;
} }
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotSupportedException(); {
throw new NotSupportedException();
} }
} }

View File

@ -10,7 +10,8 @@
<Grid Grid.Row="1" ColumnDefinitions="2*,Auto,*" RowDefinitions="*"> <Grid Grid.Row="1" ColumnDefinitions="2*,Auto,*" RowDefinitions="*">
<views:LeftTreeView Grid.Column="0" /> <views:LeftTreeView Grid.Column="0" />
<GridSplitter Grid.Column="1" Width="6" Margin="4,8" Background="{DynamicResource PanelBorderBrush}" ShowsPreview="True"/> <GridSplitter Grid.Column="1" Width="6" Margin="4,8" Background="{DynamicResource PanelBorderBrush}"
ShowsPreview="True" />
<views:RightDetailsView Grid.Column="2" /> <views:RightDetailsView Grid.Column="2" />
</Grid> </Grid>
</Grid> </Grid>

View File

@ -1,12 +1,11 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace XSDVisualiser.Desktop namespace XSDVisualiser.Desktop;
public partial class MainWindow : Window
{ {
public partial class MainWindow : Window
{
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
} }
}
} }

View File

@ -1,19 +1,19 @@
using System;
using Avalonia; using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
namespace XSDVisualiser.Desktop namespace XSDVisualiser.Desktop;
internal static class Program
{ {
internal static class Program
{
[STAThread] [STAThread]
public static void Main(string[] args) public static void Main(string[] args)
{ {
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
} }
private static AppBuilder BuildAvaloniaApp() => private static AppBuilder BuildAvaloniaApp()
AppBuilder.Configure<App>() {
return AppBuilder.Configure<App>()
.UsePlatformDetect() .UsePlatformDetect()
.LogToTrace() .LogToTrace()
.UseReactiveUI(); .UseReactiveUI();

View File

@ -1,24 +1,15 @@
using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using XSDVisualiser.Models; using XSDVisualiser.Core;
namespace XSDVisualiser.Desktop.ViewModels namespace XSDVisualiser.Desktop.ViewModels;
public class MainWindowViewModel(SchemaModel model) : INotifyPropertyChanged
{ {
public class MainWindowViewModel(SchemaModel model) : INotifyPropertyChanged private string? _currentFilePath;
{
private SchemaModel _model = model ?? throw new ArgumentNullException(nameof(model)); private SchemaModel _model = model ?? throw new ArgumentNullException(nameof(model));
private SchemaNode? _selectedRootElement;
private SchemaNode? _selectedNode; private SchemaNode? _selectedNode;
private SchemaNode? _selectedRootElement;
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public SchemaModel Model public SchemaModel Model
{ {
@ -58,9 +49,9 @@ namespace XSDVisualiser.Desktop.ViewModels
} }
} }
public IEnumerable<SchemaNode> VisibleRootElements => SelectedRootElement != null ? [SelectedRootElement] : RootElements; public IEnumerable<SchemaNode> VisibleRootElements =>
SelectedRootElement != null ? [SelectedRootElement] : RootElements;
private string? _currentFilePath;
public string? CurrentFilePath public string? CurrentFilePath
{ {
get => _currentFilePath; get => _currentFilePath;
@ -74,7 +65,16 @@ namespace XSDVisualiser.Desktop.ViewModels
} }
} }
public string? CurrentFileName => string.IsNullOrEmpty(_currentFilePath) ? null : System.IO.Path.GetFileName(_currentFilePath); public string? CurrentFileName =>
public string? CurrentDirectory => string.IsNullOrEmpty(_currentFilePath) ? null : System.IO.Path.GetDirectoryName(_currentFilePath); string.IsNullOrEmpty(_currentFilePath) ? null : Path.GetFileName(_currentFilePath);
public string? CurrentDirectory =>
string.IsNullOrEmpty(_currentFilePath) ? null : Path.GetDirectoryName(_currentFilePath);
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
} }
} }

View File

@ -8,7 +8,7 @@
FontSize="20" FontSize="20"
FontWeight="SemiBold" FontWeight="SemiBold"
Margin="0,0,12,0" Margin="0,0,12,0"
VerticalAlignment="Center"/> VerticalAlignment="Center" />
<AutoCompleteBox Grid.Column="1" <AutoCompleteBox Grid.Column="1"
Margin="0,0,12,0" Margin="0,0,12,0"
@ -20,11 +20,11 @@
VerticalAlignment="Center"> VerticalAlignment="Center">
<AutoCompleteBox.ItemTemplate> <AutoCompleteBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text="{Binding .}"/> <TextBlock Text="{Binding .}" />
</DataTemplate> </DataTemplate>
</AutoCompleteBox.ItemTemplate> </AutoCompleteBox.ItemTemplate>
<AutoCompleteBox.SelectedItem> <AutoCompleteBox.SelectedItem>
<Binding Path="SelectedRootElement" Mode="TwoWay"/> <Binding Path="SelectedRootElement" Mode="TwoWay" />
</AutoCompleteBox.SelectedItem> </AutoCompleteBox.SelectedItem>
</AutoCompleteBox> </AutoCompleteBox>
@ -32,7 +32,7 @@
Content="Open XSD and parse" Content="Open XSD and parse"
x:Name="OpenBtn" x:Name="OpenBtn"
Width="220" Width="220"
VerticalAlignment="Center"/> VerticalAlignment="Center" />
<Border Grid.Column="3" <Border Grid.Column="3"
Background="{DynamicResource PanelBackgroundBrush}" Background="{DynamicResource PanelBackgroundBrush}"
@ -46,15 +46,16 @@
ToolTip.Tip="{Binding CurrentFilePath}"> ToolTip.Tip="{Binding CurrentFilePath}">
<Border.ContextMenu> <Border.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="Copy path" Click="OnCopyPathClick"/> <MenuItem Header="Copy path" Click="OnCopyPathClick" />
<MenuItem Header="Open containing folder" Click="OnOpenContainingFolderClick"/> <MenuItem Header="Open containing folder" Click="OnOpenContainingFolderClick" />
</ContextMenu> </ContextMenu>
</Border.ContextMenu> </Border.ContextMenu>
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="File:" Foreground="{DynamicResource SubtleTextBrush}"/> <TextBlock Text="File:" Foreground="{DynamicResource SubtleTextBrush}" />
<StackPanel MaxWidth="520"> <StackPanel MaxWidth="520">
<TextBlock Text="{Binding CurrentFileName}" FontWeight="SemiBold" TextTrimming="CharacterEllipsis"/> <TextBlock Text="{Binding CurrentFileName}" FontWeight="SemiBold" TextTrimming="CharacterEllipsis" />
<TextBlock Text="{Binding CurrentDirectory}" Foreground="{DynamicResource SubtleTextBrush}" TextTrimming="CharacterEllipsis"/> <TextBlock Text="{Binding CurrentDirectory}" Foreground="{DynamicResource SubtleTextBrush}"
TextTrimming="CharacterEllipsis" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>

View File

@ -1,12 +1,9 @@
using System; using System.Diagnostics;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Threading;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading;
using XSDVisualiser.Core; using XSDVisualiser.Core;
using XSDVisualiser.Models;
using XSDVisualiser.Desktop.ViewModels; using XSDVisualiser.Desktop.ViewModels;
namespace XSDVisualiser.Desktop.Views; namespace XSDVisualiser.Desktop.Views;
@ -19,10 +16,7 @@ public partial class HeaderView : UserControl
{ {
InitializeComponent(); InitializeComponent();
_openBtn = this.FindControl<Button>("OpenBtn"); _openBtn = this.FindControl<Button>("OpenBtn");
if (_openBtn != null) if (_openBtn != null) _openBtn.Click += OpenBtn_Click;
{
_openBtn.Click += OpenBtn_Click;
}
} }
private async void OpenBtn_Click(object? sender, RoutedEventArgs e) private async void OpenBtn_Click(object? sender, RoutedEventArgs e)
@ -49,13 +43,10 @@ public partial class HeaderView : UserControl
var file = results[0]; var file = results[0];
var path = file.TryGetLocalPath(); var path = file.TryGetLocalPath();
if (!string.IsNullOrEmpty(path)) if (!string.IsNullOrEmpty(path))
{
await ParseAndShowAsync(path!, topLevel); await ParseAndShowAsync(path!, topLevel);
}
else else
{ Dispatcher.UIThread.Post(() => topLevel.DataContext = new MainWindowViewModel(new SchemaModel
Dispatcher.UIThread.Post(() => topLevel.DataContext = new MainWindowViewModel(new SchemaModel { TargetNamespace = "Selected file is not a local file. Please choose a local .xsd." })); { TargetNamespace = "Selected file is not a local file. Please choose a local .xsd." }));
}
} }
private Task ParseAndShowAsync(string path, Window hostWindow) private Task ParseAndShowAsync(string path, Window hostWindow)
@ -77,7 +68,8 @@ public partial class HeaderView : UserControl
} }
catch (Exception ex) catch (Exception ex)
{ {
Dispatcher.UIThread.Post(() => hostWindow.DataContext = new MainWindowViewModel(new SchemaModel { TargetNamespace = ex.Message })); Dispatcher.UIThread.Post(() =>
hostWindow.DataContext = new MainWindowViewModel(new SchemaModel { TargetNamespace = ex.Message }));
} }
}); });
} }
@ -89,10 +81,7 @@ public partial class HeaderView : UserControl
if (string.IsNullOrWhiteSpace(path)) return; if (string.IsNullOrWhiteSpace(path)) return;
var topLevel = TopLevel.GetTopLevel(this); var topLevel = TopLevel.GetTopLevel(this);
if (topLevel?.Clipboard != null) if (topLevel?.Clipboard != null) await topLevel.Clipboard.SetTextAsync(path);
{
await topLevel.Clipboard.SetTextAsync(path);
}
} }
private void OnOpenContainingFolderClick(object? sender, RoutedEventArgs e) private void OnOpenContainingFolderClick(object? sender, RoutedEventArgs e)
@ -100,8 +89,8 @@ public partial class HeaderView : UserControl
var vm = DataContext as MainWindowViewModel; var vm = DataContext as MainWindowViewModel;
var path = vm?.CurrentFilePath; var path = vm?.CurrentFilePath;
if (string.IsNullOrWhiteSpace(path)) return; if (string.IsNullOrWhiteSpace(path)) return;
var dir = System.IO.Path.GetDirectoryName(path); var dir = Path.GetDirectoryName(path);
if (string.IsNullOrWhiteSpace(dir) || !System.IO.Directory.Exists(dir)) return; if (string.IsNullOrWhiteSpace(dir) || !Directory.Exists(dir)) return;
TryOpenFolder(dir); TryOpenFolder(dir);
} }
@ -111,32 +100,32 @@ public partial class HeaderView : UserControl
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
var psi = new System.Diagnostics.ProcessStartInfo("explorer.exe", dir) var psi = new ProcessStartInfo("explorer.exe", dir)
{ {
UseShellExecute = true UseShellExecute = true
}; };
System.Diagnostics.Process.Start(psi); Process.Start(psi);
} }
else if (OperatingSystem.IsMacOS()) else if (OperatingSystem.IsMacOS())
{ {
System.Diagnostics.Process.Start("open", dir); Process.Start("open", dir);
} }
else else
{ {
// Linux // Linux
System.Diagnostics.Process.Start("xdg-open", dir); Process.Start("xdg-open", dir);
} }
} }
catch catch
{ {
try try
{ {
var psi = new System.Diagnostics.ProcessStartInfo var psi = new ProcessStartInfo
{ {
FileName = dir, FileName = dir,
UseShellExecute = true UseShellExecute = true
}; };
System.Diagnostics.Process.Start(psi); Process.Start(psi);
} }
catch catch
{ {

View File

@ -1,27 +1,32 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:XSDVisualiser.Models;assembly=XSDVisualiser.Core" xmlns:m="clr-namespace:XSDVisualiser.Core;assembly=XSDVisualiser.Core"
x:Class="XSDVisualiser.Desktop.Views.LeftTreeView" x:Class="XSDVisualiser.Desktop.Views.LeftTreeView"
x:CompileBindings="False"> x:CompileBindings="False">
<Border Background="{DynamicResource PanelBackgroundBrush}" <Border Background="{DynamicResource PanelBackgroundBrush}"
BorderBrush="{DynamicResource PanelBorderBrush}" BorderBrush="{DynamicResource PanelBorderBrush}"
BorderThickness="1" CornerRadius="6" Margin="0,8,8,0"> BorderThickness="1" CornerRadius="6" Margin="0,8,8,0">
<ScrollViewer> <ScrollViewer>
<TreeView x:Name="SchemaTree" ItemsSource="{Binding VisibleRootElements}" SelectedItem="{Binding SelectedNode, Mode=TwoWay}"> <TreeView x:Name="SchemaTree" ItemsSource="{Binding VisibleRootElements}"
SelectedItem="{Binding SelectedNode, Mode=TwoWay}">
<TreeView.DataTemplates> <TreeView.DataTemplates>
<TreeDataTemplate DataType="{x:Type m:SchemaNode}" ItemsSource="{Binding Children}"> <TreeDataTemplate DataType="{x:Type m:SchemaNode}" ItemsSource="{Binding Children}">
<StackPanel> <StackPanel>
<!-- Connector line from parent to this node (hidden for roots where ContentModel is null) --> <!-- Connector line from parent to this node (hidden for roots where ContentModel is null) -->
<Grid Margin="0,8,8,2" IsVisible="{Binding ContentModel, Converter={x:Static ObjectConverters.IsNotNull}}" <Grid Margin="0,8,8,2"
IsVisible="{Binding ContentModel, Converter={x:Static ObjectConverters.IsNotNull}}"
ColumnDefinitions="16,*"> ColumnDefinitions="16,*">
<!-- leading small elbow --> <!-- leading small elbow -->
<Rectangle Grid.Column="0" Width="16" Height="2" Fill="{DynamicResource SeparatorBrush}" VerticalAlignment="Center"/> <Rectangle Grid.Column="0" Width="16" Height="2"
Fill="{DynamicResource SeparatorBrush}" VerticalAlignment="Center" />
<Grid Grid.Column="1"> <Grid Grid.Column="1">
<Rectangle Height="2" Fill="{DynamicResource SeparatorBrush}" VerticalAlignment="Center"/> <Rectangle Height="2" Fill="{DynamicResource SeparatorBrush}"
<TextBlock Text="{Binding Cardinality, Converter={StaticResource CardinalityToLabel}}" VerticalAlignment="Center" />
<TextBlock
Text="{Binding Cardinality, Converter={StaticResource CardinalityToLabel}}"
Background="{DynamicResource BadgeBackgroundBrush}" Padding="6,0" Background="{DynamicResource BadgeBackgroundBrush}" Padding="6,0"
Foreground="{DynamicResource SubtleTextBrush}" Foreground="{DynamicResource SubtleTextBrush}"
HorizontalAlignment="Center"/> HorizontalAlignment="Center" />
</Grid> </Grid>
</Grid> </Grid>
@ -32,28 +37,28 @@
Padding="8" Margin="0,0,8,6" Background="{DynamicResource PanelBackgroundBrush}"> Padding="8" Margin="0,0,8,6" Background="{DynamicResource PanelBackgroundBrush}">
<Border.ContextMenu> <Border.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="Copy as TSV" Click="OnCopyAsTsvClick"/> <MenuItem Header="Copy as TSV" Click="OnCopyAsTsvClick" />
</ContextMenu> </ContextMenu>
</Border.ContextMenu> </Border.ContextMenu>
<StackPanel Orientation="Vertical" Spacing="2"> <StackPanel Orientation="Vertical" Spacing="2">
<!-- Name on its own line, prominent --> <!-- Name on its own line, prominent -->
<TextBlock Text="{Binding Name}" FontWeight="SemiBold"/> <TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
<!-- Path/Namespace on its own line --> <!-- Path/Namespace on its own line -->
<TextBlock Text="{Binding Namespace, StringFormat=Namespace: {0}}" <TextBlock Text="{Binding Namespace, StringFormat=Namespace: {0}}"
Foreground="{DynamicResource SubtleTextBrush}"/> Foreground="{DynamicResource SubtleTextBrush}" />
<!-- Type info on its own line (TypeName with fallback to BuiltInType) --> <!-- Type info on its own line (TypeName with fallback to BuiltInType) -->
<StackPanel Orientation="Horizontal" Spacing="6"> <StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock Text="Type:" FontWeight="Medium"/> <TextBlock Text="Type:" FontWeight="Medium" />
<TextBlock Text="{Binding TypeName}"/> <TextBlock Text="{Binding TypeName}" />
<TextBlock Text="{Binding BuiltInType, StringFormat=({0})}" <TextBlock Text="{Binding BuiltInType, StringFormat=({0})}"
Foreground="{DynamicResource MutedTextBrush}"/> Foreground="{DynamicResource MutedTextBrush}" />
</StackPanel> </StackPanel>
<!-- Optional: show content model as a subtle hint --> <!-- Optional: show content model as a subtle hint -->
<TextBlock Text="{Binding ContentModel, StringFormat=Model: {0}}" <TextBlock Text="{Binding ContentModel, StringFormat=Model: {0}}"
Foreground="{DynamicResource SubtleTextBrush}" FontWeight="SemiBold"/> Foreground="{DynamicResource SubtleTextBrush}" FontWeight="SemiBold" />
</StackPanel> </StackPanel>
</Border> </Border>
</StackPanel> </StackPanel>

View File

@ -1,15 +1,14 @@
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using XSDVisualiser.Models; using XSDVisualiser.Core;
namespace XSDVisualiser.Desktop.Views namespace XSDVisualiser.Desktop.Views;
public partial class LeftTreeView : UserControl
{ {
public partial class LeftTreeView : UserControl
{
public LeftTreeView() public LeftTreeView()
{ {
InitializeComponent(); InitializeComponent();
@ -20,24 +19,23 @@ namespace XSDVisualiser.Desktop.Views
// Determine the node from the menu item's DataContext // Determine the node from the menu item's DataContext
var node = (sender as MenuItem)?.DataContext as SchemaNode; var node = (sender as MenuItem)?.DataContext as SchemaNode;
if (node == null) if (node == null)
node = (sender as Control)?.GetVisualAncestors().OfType<Control>().FirstOrDefault()?.DataContext as SchemaNode; node =
(sender as Control)?.GetVisualAncestors().OfType<Control>().FirstOrDefault()?.DataContext as SchemaNode;
if (node == null) if (node == null)
return; return;
var tsv = BuildTsv(node); var tsv = BuildTsv(node);
var topLevel = TopLevel.GetTopLevel(this); var topLevel = TopLevel.GetTopLevel(this);
if (topLevel?.Clipboard != null) if (topLevel?.Clipboard != null) await topLevel.Clipboard.SetTextAsync(tsv);
{
await topLevel.Clipboard.SetTextAsync(tsv);
}
} }
private static string BuildTsv(SchemaNode root) private static string BuildTsv(SchemaNode root)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
// Header // Header
sb.AppendLine("Depth\tPath\tName\tNamespace\tTypeName\tBuiltInType\tMinOccurs\tMaxOccurs\tContentModel\tConstraints\tIsNillable"); sb.AppendLine(
"Depth\tPath\tName\tNamespace\tTypeName\tBuiltInType\tMinOccurs\tMaxOccurs\tContentModel\tConstraints\tIsNillable");
var initialPath = root.Name ?? string.Empty; var initialPath = root.Name ?? string.Empty;
AppendNode(sb, root, 0, initialPath); AppendNode(sb, root, 0, initialPath);
return sb.ToString(); return sb.ToString();
@ -75,7 +73,6 @@ namespace XSDVisualiser.Desktop.Views
sb.AppendLine(line); sb.AppendLine(line);
if (node.Children != null) if (node.Children != null)
{
foreach (var child in node.Children) foreach (var child in node.Children)
{ {
var nextPathSegment = child.Name ?? string.Empty; var nextPathSegment = child.Name ?? string.Empty;
@ -83,14 +80,13 @@ namespace XSDVisualiser.Desktop.Views
AppendNode(sb, child, depth + 1, childPath); AppendNode(sb, child, depth + 1, childPath);
} }
} }
}
private static string FormatConstraints(SchemaNode node) private static string FormatConstraints(SchemaNode node)
{ {
var cons = node.Constraints?.Constraints; var cons = node.Constraints?.Constraints;
if (cons == null || cons.Count == 0) return string.Empty; if (cons == null || cons.Count == 0) return string.Empty;
var dict = new System.Collections.Generic.SortedDictionary<string, System.Collections.Generic.SortedSet<string>>(System.StringComparer.Ordinal); var dict = new SortedDictionary<string, SortedSet<string>>(StringComparer.Ordinal);
foreach (var c in cons) foreach (var c in cons)
{ {
var name = c.Name; var name = c.Name;
@ -98,20 +94,22 @@ namespace XSDVisualiser.Desktop.Views
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) continue; if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) continue;
if (!dict.TryGetValue(name, out var set)) if (!dict.TryGetValue(name, out var set))
{ {
set = new System.Collections.Generic.SortedSet<string>(System.StringComparer.Ordinal); set = new SortedSet<string>(StringComparer.Ordinal);
dict[name] = set; dict[name] = set;
} }
set.Add(value); set.Add(value);
} }
if (dict.Count == 0) return string.Empty; if (dict.Count == 0) return string.Empty;
var parts = new System.Collections.Generic.List<string>(dict.Count); var parts = new List<string>(dict.Count);
foreach (var kvp in dict) foreach (var kvp in dict)
{ {
var joinedValues = string.Join(",", kvp.Value); var joinedValues = string.Join(",", kvp.Value);
parts.Add($"{kvp.Key}={joinedValues}"); parts.Add($"{kvp.Key}={joinedValues}");
} }
return string.Join(";", parts); return string.Join(";", parts);
} }
}
} }

View File

@ -1,6 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:XSDVisualiser.Models;assembly=XSDVisualiser.Core" xmlns:core="clr-namespace:XSDVisualiser.Core;assembly=XSDVisualiser.Core"
x:Class="XSDVisualiser.Desktop.Views.RightDetailsView" x:Class="XSDVisualiser.Desktop.Views.RightDetailsView"
x:CompileBindings="False"> x:CompileBindings="False">
<Border Background="{DynamicResource PanelBackgroundBrush}" <Border Background="{DynamicResource PanelBackgroundBrush}"
@ -9,64 +9,72 @@
<ScrollViewer> <ScrollViewer>
<ContentControl Content="{Binding SelectedNode}"> <ContentControl Content="{Binding SelectedNode}">
<ContentControl.DataTemplates> <ContentControl.DataTemplates>
<DataTemplate DataType="{x:Type m:SchemaNode}"> <DataTemplate DataType="{x:Type core:SchemaNode}">
<StackPanel Margin="12" Spacing="12"> <StackPanel Margin="12" Spacing="12">
<TextBlock Text="Details" FontSize="16" FontWeight="SemiBold"/> <TextBlock Text="Details" FontSize="16" FontWeight="SemiBold" />
<!-- General info --> <!-- General info -->
<Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1" CornerRadius="4" Padding="10"> <Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1"
CornerRadius="4" Padding="10">
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*"> <Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" FontWeight="Bold"/> <TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" FontWeight="Bold" />
<TextBlock Grid.Row="0" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Name}"/> <TextBlock Grid.Row="0" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Name}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Built-in:" FontWeight="Bold"/> <TextBlock Grid.Row="1" Grid.Column="0" Text="Built-in:" FontWeight="Bold" />
<TextBlock Grid.Row="1" Grid.Column="1" Margin="8,0,0,0" Text="{Binding BuiltInType}" FontWeight="Bold"/> <TextBlock Grid.Row="1" Grid.Column="1" Margin="8,0,0,0"
Text="{Binding BuiltInType}" FontWeight="Bold" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="Model:" FontWeight="Bold"/> <TextBlock Grid.Row="2" Grid.Column="0" Text="Model:" FontWeight="Bold" />
<TextBlock Grid.Row="2" Grid.Column="1" Margin="8,0,0,0" Text="{Binding ContentModel}"/> <TextBlock Grid.Row="2" Grid.Column="1" Margin="8,0,0,0"
Text="{Binding ContentModel}" />
<TextBlock Grid.Row="3" Grid.Column="0" Text="Namespace:" FontWeight="Bold"/> <TextBlock Grid.Row="3" Grid.Column="0" Text="Namespace:" FontWeight="Bold" />
<TextBlock Grid.Row="3" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Namespace}"/> <TextBlock Grid.Row="3" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Namespace}" />
<TextBlock Grid.Row="4" Grid.Column="0" Text="Cardinality:" FontWeight="Bold"/> <TextBlock Grid.Row="4" Grid.Column="0" Text="Cardinality:" FontWeight="Bold" />
<TextBlock Grid.Row="4" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Cardinality, Converter={StaticResource CardinalityToLabel}}"/> <TextBlock Grid.Row="4" Grid.Column="1" Margin="8,0,0,0"
Text="{Binding Cardinality, Converter={StaticResource CardinalityToLabel}}" />
<TextBlock Grid.Row="5" Grid.Column="0" Text="Nillable:" FontWeight="Bold"/> <TextBlock Grid.Row="5" Grid.Column="0" Text="Nillable:" FontWeight="Bold" />
<TextBlock Grid.Row="5" Grid.Column="1" Margin="8,0,0,0" Text="{Binding IsNillable}"/> <TextBlock Grid.Row="5" Grid.Column="1" Margin="8,0,0,0"
Text="{Binding IsNillable}" />
<TextBlock Grid.Row="6" Grid.Column="0" Text="Type:" FontWeight="Bold"/> <TextBlock Grid.Row="6" Grid.Column="0" Text="Type:" FontWeight="Bold" />
<TextBlock Grid.Row="6" Grid.Column="1" Margin="8,0,0,0" Text="{Binding TypeName}"/> <TextBlock Grid.Row="6" Grid.Column="1" Margin="8,0,0,0" Text="{Binding TypeName}" />
</Grid> </Grid>
</Border> </Border>
<!-- Documentation --> <!-- Documentation -->
<StackPanel IsVisible="{Binding Documentation, Converter={x:Static ObjectConverters.IsNotNull}}"> <StackPanel
<TextBlock Text="Documentation" FontWeight="SemiBold"/> IsVisible="{Binding Documentation, Converter={x:Static ObjectConverters.IsNotNull}}">
<Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1" CornerRadius="4" Padding="10"> <TextBlock Text="Documentation" FontWeight="SemiBold" />
<TextBlock Text="{Binding Documentation}" TextWrapping="Wrap"/> <Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1"
CornerRadius="4" Padding="10">
<TextBlock Text="{Binding Documentation}" TextWrapping="Wrap" />
</Border> </Border>
</StackPanel> </StackPanel>
<!-- Constraints --> <!-- Constraints -->
<StackPanel> <StackPanel>
<TextBlock Text="Constraints" FontWeight="SemiBold"/> <TextBlock Text="Constraints" FontWeight="SemiBold" />
<ContentControl Content="{Binding Constraints}"> <ContentControl Content="{Binding Constraints}">
<ContentControl.DataTemplates> <ContentControl.DataTemplates>
<DataTemplate x:DataType="m:ConstraintSet"> <DataTemplate x:DataType="core:ConstraintSet">
<StackPanel Spacing="8"> <StackPanel Spacing="8">
<TextBlock Text="Base Type:" FontWeight="Bold"/> <TextBlock Text="Base Type:" FontWeight="Bold" />
<TextBlock Text="{Binding BaseTypeName}"/> <TextBlock Text="{Binding BaseTypeName}" />
<StackPanel> <StackPanel>
<TextBlock Text="Constraints" FontWeight="SemiBold"/> <TextBlock Text="Constraints" FontWeight="SemiBold" />
<ItemsControl ItemsSource="{Binding Constraints}" IsVisible="{Binding Constraints, Converter={StaticResource HasItems}}"> <ItemsControl ItemsSource="{Binding Constraints}"
IsVisible="{Binding Constraints, Converter={StaticResource HasItems}}">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate x:DataType="m:ConstraintEntry"> <DataTemplate x:DataType="core:ConstraintEntry">
<TextBlock> <TextBlock>
<Run Text="• "/> <Run Text="• " />
<Run Text="{Binding Name}"/> <Run Text="{Binding Name}" />
<Run Text=": "/> <Run Text=": " />
<Run Text="{Binding Value}"/> <Run Text="{Binding Value}" />
</TextBlock> </TextBlock>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>

View File

@ -1,12 +1,11 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace XSDVisualiser.Desktop.Views namespace XSDVisualiser.Desktop.Views;
public partial class RightDetailsView : UserControl
{ {
public partial class RightDetailsView : UserControl
{
public RightDetailsView() public RightDetailsView()
{ {
InitializeComponent(); InitializeComponent();
} }
}
} }

View File

@ -7,13 +7,13 @@
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.1.3" /> <PackageReference Include="Avalonia" Version="11.1.3"/>
<PackageReference Include="Avalonia.Desktop" Version="11.1.3" /> <PackageReference Include="Avalonia.Desktop" Version="11.1.3"/>
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.3" /> <PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.3"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.3" /> <PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.3"/>
<PackageReference Include="Avalonia.ReactiveUI" Version="11.1.3" /> <PackageReference Include="Avalonia.ReactiveUI" Version="11.1.3"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\XSDVisualiser.Core\XSDVisualiser.Core.csproj" /> <ProjectReference Include="..\XSDVisualiser.Core\XSDVisualiser.Core.csproj"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,21 @@
using System.Xml.Serialization;
namespace XSDVisualiser.Core;
/// <summary>
/// Attribute definition extracted from XSD.
/// </summary>
public class AttributeInfo
{
[XmlAttribute] public string? Name { get; set; }
[XmlAttribute] public string? Namespace { get; set; }
[XmlAttribute] public string? Use { get; set; } // optional | required | prohibited
[XmlAttribute] public string? TypeName { get; set; }
[XmlAttribute] public string? BuiltInType { get; set; }
[XmlElement] public ConstraintSet? Constraints { get; set; }
}

View File

@ -0,0 +1,10 @@
using System.Xml.Serialization;
namespace XSDVisualiser.Core;
public class ConstraintEntry
{
[XmlAttribute] public string? Name { get; set; }
[XmlAttribute] public string? Value { get; set; }
}

View File

@ -0,0 +1,16 @@
using System.Xml.Serialization;
namespace XSDVisualiser.Core;
/// <summary>
/// SimpleType constraints (formerly called facets).
/// </summary>
public class ConstraintSet
{
[XmlAttribute] public string? BaseTypeName { get; set; }
// Generic catch-all list of constraints for dynamic display and tooling
[XmlArray("Constraints")]
[XmlArrayItem("Constraint")]
public List<ConstraintEntry> Constraints { get; set; } = new();
}

View File

@ -0,0 +1,19 @@
using System.Xml.Serialization;
namespace XSDVisualiser.Core;
/// <summary>
/// Min/Max occurrences.
/// </summary>
public class Occurs
{
[XmlAttribute] public decimal Min { get; set; } = 1;
/// <summary>
/// If MaxIsUnbounded is true, Max is ignored.
/// </summary>
[XmlAttribute]
public decimal Max { get; set; } = 1;
[XmlAttribute] public bool MaxIsUnbounded { get; set; }
}

View File

@ -0,0 +1,16 @@
using System.Xml.Serialization;
namespace XSDVisualiser.Core;
/// <summary>
/// Root of the parsed XSD representation.
/// </summary>
[XmlRoot("SchemaModel")]
public class SchemaModel
{
[XmlAttribute] public string? TargetNamespace { get; set; }
[XmlArray("RootElements")]
[XmlArrayItem("Element")]
public List<SchemaNode> RootElements { get; set; } = new();
}

View File

@ -0,0 +1,51 @@
using System.Xml.Serialization;
namespace XSDVisualiser.Core;
/// <summary>
/// Represents an element or complex content node in the schema.
/// </summary>
public class SchemaNode
{
[XmlAttribute] public string? Name { get; set; }
[XmlAttribute] public string? Namespace { get; set; }
[XmlAttribute] public string? TypeName { get; set; }
[XmlAttribute] public string? BuiltInType { get; set; }
[XmlAttribute] public bool IsNillable { get; set; }
[XmlElement] public Occurs Cardinality { get; set; } = new();
[XmlElement] public ConstraintSet? Constraints { get; set; }
[XmlArray("Attributes")]
[XmlArrayItem("Attribute")]
public List<AttributeInfo> Attributes { get; set; } = new();
[XmlArray("Children")]
[XmlArrayItem("Element")]
public List<SchemaNode> Children { get; set; } = new();
[XmlAttribute] public string? ContentModel { get; set; } // sequence | choice | all | simple
/// <summary>
/// Human-readable documentation extracted from xsd:annotation/xsd:documentation.
/// Prefer element-level documentation; falls back to type-level documentation.
/// </summary>
[XmlElement]
public string? Documentation { get; set; }
public override string ToString()
{
// Used by AutoCompleteBox to get text for filtering and matching
// Prefer Name, then TypeName, and fall back to base implementation
if (!string.IsNullOrEmpty(Name))
return Name!;
if (!string.IsNullOrEmpty(TypeName))
return TypeName!;
return base.ToString();
}
}

View File

@ -1,140 +0,0 @@
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace XSDVisualiser.Models
{
/// <summary>
/// Root of the parsed XSD representation.
/// </summary>
[XmlRoot("SchemaModel")]
public class SchemaModel
{
[XmlAttribute]
public string? TargetNamespace { get; set; }
[XmlArray("RootElements")]
[XmlArrayItem("Element")]
public List<SchemaNode> RootElements { get; set; } = new();
}
/// <summary>
/// Represents an element or complex content node in the schema.
/// </summary>
public class SchemaNode
{
[XmlAttribute]
public string? Name { get; set; }
[XmlAttribute]
public string? Namespace { get; set; }
[XmlAttribute]
public string? TypeName { get; set; }
[XmlAttribute]
public string? BuiltInType { get; set; }
[XmlAttribute]
public bool IsNillable { get; set; }
[XmlElement]
public Occurs Cardinality { get; set; } = new();
[XmlElement]
public ConstraintSet? Constraints { get; set; }
[XmlArray("Attributes")]
[XmlArrayItem("Attribute")]
public List<AttributeInfo> Attributes { get; set; } = new();
[XmlArray("Children")]
[XmlArrayItem("Element")]
public List<SchemaNode> Children { get; set; } = new();
[XmlAttribute]
public string? ContentModel { get; set; } // sequence | choice | all | simple
/// <summary>
/// Human-readable documentation extracted from xsd:annotation/xsd:documentation.
/// Prefer element-level documentation; falls back to type-level documentation.
/// </summary>
[XmlElement]
public string? Documentation { get; set; }
public override string ToString()
{
// Used by AutoCompleteBox to get text for filtering and matching
// Prefer Name, then TypeName, and fall back to base implementation
if (!string.IsNullOrEmpty(Name))
return Name!;
if (!string.IsNullOrEmpty(TypeName))
return TypeName!;
return base.ToString();
}
}
/// <summary>
/// Min/Max occurrences.
/// </summary>
public class Occurs
{
[XmlAttribute]
public decimal Min { get; set; } = 1;
/// <summary>
/// If MaxIsUnbounded is true, Max is ignored.
/// </summary>
[XmlAttribute]
public decimal Max { get; set; } = 1;
[XmlAttribute]
public bool MaxIsUnbounded { get; set; }
}
/// <summary>
/// Attribute definition extracted from XSD.
/// </summary>
public class AttributeInfo
{
[XmlAttribute]
public string? Name { get; set; }
[XmlAttribute]
public string? Namespace { get; set; }
[XmlAttribute]
public string? Use { get; set; } // optional | required | prohibited
[XmlAttribute]
public string? TypeName { get; set; }
[XmlAttribute]
public string? BuiltInType { get; set; }
[XmlElement]
public ConstraintSet? Constraints { get; set; }
}
/// <summary>
/// SimpleType constraints (formerly called facets).
/// </summary>
public class ConstraintSet
{
[XmlAttribute]
public string? BaseTypeName { get; set; }
// Generic catch-all list of constraints for dynamic display and tooling
[XmlArray("Constraints")]
[XmlArrayItem("Constraint")]
public List<ConstraintEntry> Constraints { get; set; } = new();
}
public class ConstraintEntry
{
[XmlAttribute]
public string? Name { get; set; }
[XmlAttribute]
public string? Value { get; set; }
}
}

View File

@ -1,12 +1,11 @@
using System.Text;
using System.Xml; using System.Xml;
using System.Xml.Schema; using System.Xml.Schema;
using System.Text;
using XSDVisualiser.Models;
namespace XSDVisualiser.Core namespace XSDVisualiser.Core;
public class XsdSchemaParser
{ {
public class XsdSchemaParser
{
private readonly XmlSchemaSet _set = new(); private readonly XmlSchemaSet _set = new();
public SchemaModel Parse(string xsdPath) public SchemaModel Parse(string xsdPath)
@ -26,7 +25,7 @@ namespace XSDVisualiser.Core
foreach (var globalEl in _set.Schemas().Cast<XmlSchema>() foreach (var globalEl in _set.Schemas().Cast<XmlSchema>()
.SelectMany(s => s.Elements.Values.Cast<XmlSchemaElement>())) .SelectMany(s => s.Elements.Values.Cast<XmlSchemaElement>()))
{ {
var node = BuildNodeForElement(globalEl, parentContentModel: null); var node = BuildNodeForElement(globalEl, null);
model.RootElements.Add(node); model.RootElements.Add(node);
} }
@ -61,14 +60,10 @@ namespace XSDVisualiser.Core
var type = ResolveElementType(element); var type = ResolveElementType(element);
if (type == null) return node; if (type == null) return node;
node.TypeName = GetQualifiedTypeName(type); node.TypeName = GetQualifiedTypeName(type);
if (type.Datatype != null) if (type.Datatype != null) node.BuiltInType = type.Datatype.TypeCode.ToString();
{
node.BuiltInType = type.Datatype.TypeCode.ToString();
}
// Fallback to type-level documentation if none on element // Fallback to type-level documentation if none on element
if (string.IsNullOrWhiteSpace(node.Documentation)) if (string.IsNullOrWhiteSpace(node.Documentation))
{
switch (type) switch (type)
{ {
case XmlSchemaComplexType ctDoc: case XmlSchemaComplexType ctDoc:
@ -78,7 +73,6 @@ namespace XSDVisualiser.Core
node.Documentation = ExtractDocumentation(stDoc); node.Documentation = ExtractDocumentation(stDoc);
break; break;
} }
}
switch (type) switch (type)
{ {
@ -105,10 +99,7 @@ namespace XSDVisualiser.Core
private XmlSchemaType? ResolveElementType(XmlSchemaElement el) private XmlSchemaType? ResolveElementType(XmlSchemaElement el)
{ {
if (el.ElementSchemaType != null) return el.ElementSchemaType; if (el.ElementSchemaType != null) return el.ElementSchemaType;
if (!el.SchemaTypeName.IsEmpty) if (!el.SchemaTypeName.IsEmpty) return _set.GlobalTypes[el.SchemaTypeName] as XmlSchemaType;
{
return _set.GlobalTypes[el.SchemaTypeName] as XmlSchemaType;
}
return el.SchemaType; return el.SchemaType;
} }
@ -123,10 +114,7 @@ namespace XSDVisualiser.Core
{ {
var qn = attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName; var qn = attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName;
var key = qn.ToString(); var key = qn.ToString();
if (seenAttrKeys.Add(key)) if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(attr));
{
node.Attributes.Add(ExtractAttribute(attr));
}
} }
// 2) Uncompiled attributes directly on the type (fallback) // 2) Uncompiled attributes directly on the type (fallback)
@ -134,15 +122,11 @@ namespace XSDVisualiser.Core
{ {
var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName; var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName;
var key = qn.ToString(); var key = qn.ToString();
if (seenAttrKeys.Add(key)) if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(a));
{
node.Attributes.Add(ExtractAttribute(a));
}
} }
// 3) Attributes from complexContent extension/restriction // 3) Attributes from complexContent extension/restriction
if (ct.ContentModel is XmlSchemaComplexContent cc) if (ct.ContentModel is XmlSchemaComplexContent cc)
{
switch (cc.Content) switch (cc.Content)
{ {
case XmlSchemaComplexContentExtension cext: case XmlSchemaComplexContentExtension cext:
@ -151,10 +135,7 @@ namespace XSDVisualiser.Core
{ {
var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName; var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName;
var key = qn.ToString(); var key = qn.ToString();
if (seenAttrKeys.Add(key)) if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(a));
{
node.Attributes.Add(ExtractAttribute(a));
}
} }
break; break;
@ -165,16 +146,12 @@ namespace XSDVisualiser.Core
{ {
var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName; var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName;
var key = qn.ToString(); var key = qn.ToString();
if (seenAttrKeys.Add(key)) if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(a));
{
node.Attributes.Add(ExtractAttribute(a));
}
} }
break; break;
} }
} }
}
// Content model // Content model
if (ct.ContentTypeParticle is XmlSchemaGroupBase group) if (ct.ContentTypeParticle is XmlSchemaGroupBase group)
@ -189,7 +166,6 @@ namespace XSDVisualiser.Core
node.ContentModel = content; node.ContentModel = content;
foreach (var item in group.Items) foreach (var item in group.Items)
{
switch (item) switch (item)
{ {
case XmlSchemaElement childEl: case XmlSchemaElement childEl:
@ -208,22 +184,25 @@ namespace XSDVisualiser.Core
XmlSchemaAll => "all", XmlSchemaAll => "all",
_ => "group" _ => "group"
}, },
Cardinality = new Occurs { Min = nestedGroup.MinOccurs, Max = nestedGroup.MaxOccurs, MaxIsUnbounded = nestedGroup.MaxOccursString == "unbounded" } Cardinality = new Occurs
{
Min = nestedGroup.MinOccurs, Max = nestedGroup.MaxOccurs,
MaxIsUnbounded = nestedGroup.MaxOccursString == "unbounded"
}
}; };
foreach (var nestedItem in nestedGroup.Items) foreach (var nestedItem in nestedGroup.Items)
{
if (nestedItem is XmlSchemaElement ngChild) if (nestedItem is XmlSchemaElement ngChild)
{
synthetic.Children.Add(BuildNodeForElement(ngChild, synthetic.ContentModel)); synthetic.Children.Add(BuildNodeForElement(ngChild, synthetic.ContentModel));
}
}
node.Children.Add(synthetic); node.Children.Add(synthetic);
break; break;
// Skip other particles for now // Skip other particles for now
} }
} }
} else if (ct is
else if (ct.ContentType == XmlSchemaContentType.TextOnly && ct.ContentModel is XmlSchemaSimpleContent simpleContent) {
ContentType: XmlSchemaContentType.TextOnly, ContentModel: XmlSchemaSimpleContent simpleContent
})
{ {
node.ContentModel = "simple"; node.ContentModel = "simple";
switch (simpleContent.Content) switch (simpleContent.Content)
@ -242,10 +221,7 @@ namespace XSDVisualiser.Core
{ {
var qn = attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName; var qn = attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName;
var key = qn.ToString(); var key = qn.ToString();
if (seenAttrKeys.Add(key)) if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(attr));
{
node.Attributes.Add(ExtractAttribute(attr));
}
} }
break; break;
@ -284,7 +260,7 @@ namespace XSDVisualiser.Core
}; };
XmlSchemaSimpleType? st = null; XmlSchemaSimpleType? st = null;
if (attr.AttributeSchemaType != null) st = attr.AttributeSchemaType as XmlSchemaSimpleType; if (attr.AttributeSchemaType != null) st = attr.AttributeSchemaType;
else if (!attr.SchemaTypeName.IsEmpty) st = ResolveType(attr.SchemaTypeName) as XmlSchemaSimpleType; else if (!attr.SchemaTypeName.IsEmpty) st = ResolveType(attr.SchemaTypeName) as XmlSchemaSimpleType;
else if (attr.SchemaType != null) st = attr.SchemaType; else if (attr.SchemaType != null) st = attr.SchemaType;
@ -326,12 +302,10 @@ namespace XSDVisualiser.Core
{ {
foreach (var memberType in union.BaseMemberTypes) foreach (var memberType in union.BaseMemberTypes)
{ {
if (memberType is { } mst) if (memberType is not { } mst) continue;
{
var sub = ExtractConstraints(mst); var sub = ExtractConstraints(mst);
Merge(cons, sub); Merge(cons, sub);
} }
}
break; break;
} }
@ -342,26 +316,15 @@ namespace XSDVisualiser.Core
private static void Merge(ConstraintSet target, ConstraintSet? source) private static void Merge(ConstraintSet target, ConstraintSet? source)
{ {
if (source == null) return;
// Merge generic constraints (name + value de-duplication) // Merge generic constraints (name + value de-duplication)
if (source.Constraints != null) if (source?.Constraints == null) return;
{
foreach (var sc in source.Constraints) foreach (var sc in source.Constraints)
{ {
var exists = false; var exists = target.Constraints.Any(tc =>
foreach (var tc in target.Constraints) string.Equals(tc.Name, sc.Name, StringComparison.Ordinal) &&
{ string.Equals(tc.Value, sc.Value, StringComparison.Ordinal));
if (string.Equals(tc.Name, sc.Name, StringComparison.Ordinal) && string.Equals(tc.Value, sc.Value, StringComparison.Ordinal)) if (!exists) target.Constraints.Add(new ConstraintEntry { Name = sc.Name, Value = sc.Value });
{
exists = true; break;
}
}
if (!exists)
{
target.Constraints.Add(new ConstraintEntry { Name = sc.Name, Value = sc.Value });
}
}
} }
} }
@ -370,26 +333,16 @@ namespace XSDVisualiser.Core
foreach (var f in facets) foreach (var f in facets)
{ {
// Capture all constraints generically for dynamic display // Capture all constraints generically for dynamic display
if (f is XmlSchemaFacet baseFacet) if (f is not XmlSchemaFacet baseFacet) continue;
{
var name = GetFacetName(f); var name = GetFacetName(f);
var value = baseFacet.Value; var value = baseFacet.Value;
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) continue;
{
var exists = false; var exists = cons.Constraints.Any(entry =>
foreach (var entry in cons.Constraints) string.Equals(entry.Name, name, StringComparison.Ordinal) &&
{ string.Equals(entry.Value, value, StringComparison.Ordinal));
if (string.Equals(entry.Name, name, StringComparison.Ordinal) && string.Equals(entry.Value, value, StringComparison.Ordinal)) if (!exists) cons.Constraints.Add(new ConstraintEntry { Name = name, Value = value });
{
exists = true; break;
}
}
if (!exists)
{
cons.Constraints.Add(new ConstraintEntry { Name = name, Value = value });
}
}
}
} }
} }
@ -403,6 +356,7 @@ namespace XSDVisualiser.Core
if (typeName.Length == 0) return typeName; if (typeName.Length == 0) return typeName;
return char.ToLowerInvariant(typeName[0]) + typeName.Substring(1); return char.ToLowerInvariant(typeName[0]) + typeName.Substring(1);
} }
private static string? ExtractDocumentation(XmlSchemaAnnotated? annotated) private static string? ExtractDocumentation(XmlSchemaAnnotated? annotated)
{ {
if (annotated?.Annotation == null) return null; if (annotated?.Annotation == null) return null;
@ -415,20 +369,15 @@ namespace XSDVisualiser.Core
{ {
var pieceBuilder = new StringBuilder(); var pieceBuilder = new StringBuilder();
foreach (var node in doc.Markup) foreach (var node in doc.Markup)
{
try try
{ {
var text = node?.InnerText; var text = node?.InnerText;
if (!string.IsNullOrWhiteSpace(text)) if (!string.IsNullOrWhiteSpace(text)) pieceBuilder.Append(text);
{
pieceBuilder.Append(text);
}
} }
catch catch
{ {
// ignore malformed nodes // ignore malformed nodes
} }
}
var piece = pieceBuilder.ToString().Trim(); var piece = pieceBuilder.ToString().Trim();
if (string.IsNullOrWhiteSpace(piece)) continue; if (string.IsNullOrWhiteSpace(piece)) continue;
@ -444,5 +393,4 @@ namespace XSDVisualiser.Core
return sb.Length == 0 ? null : sb.ToString(); return sb.Length == 0 ? null : sb.ToString();
} }
}
} }

View File

@ -10,7 +10,8 @@ if (args.Length == 0 || args[0] is "-h" or "--help")
var inputPath = args[0]; var inputPath = args[0];
*/ */
const string inputPath = "/home/frederik/Work/XSDVisualiser/XSDVisualiser/Rescources/SF2900/SF2900_EP_MS1-2/DistributionServiceMsgV2.xsd"; const string inputPath =
"/home/frederik/Work/XSDVisualiser/XSDVisualiser/Rescources/SF2900/SF2900_EP_MS1-2/DistributionServiceMsgV2.xsd";
if (!File.Exists(inputPath)) if (!File.Exists(inputPath))
{ {
@ -20,8 +21,7 @@ if (!File.Exists(inputPath))
var format = "json"; var format = "json";
string? outPath = null; string? outPath = null;
for (int i = 1; i < args.Length; i++) for (var i = 1; i < args.Length; i++)
{
switch (args[i]) switch (args[i])
{ {
case "--format": case "--format":
@ -34,6 +34,7 @@ for (int i = 1; i < args.Length; i++)
Console.Error.WriteLine("--format requires a value: xml or json"); Console.Error.WriteLine("--format requires a value: xml or json");
Environment.Exit(2); Environment.Exit(2);
} }
break; break;
case "--out": case "--out":
if (i + 1 < args.Length) if (i + 1 < args.Length)
@ -45,16 +46,16 @@ for (int i = 1; i < args.Length; i++)
Console.Error.WriteLine("--out requires a file path"); Console.Error.WriteLine("--out requires a file path");
Environment.Exit(2); Environment.Exit(2);
} }
break; break;
} }
}
try try
{ {
var parser = new XsdSchemaParser(); var parser = new XsdSchemaParser();
var model = parser.Parse(inputPath); var model = parser.Parse(inputPath);
string output = format switch var output = format switch
{ {
"json" => Serialization.ToJson(model), "json" => Serialization.ToJson(model),
_ => Serialization.ToXml(model) _ => Serialization.ToXml(model)
@ -72,11 +73,8 @@ try
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.Error.WriteLine("Error: " + ex.ToString()); Console.Error.WriteLine("Error: " + ex);
if (ex.InnerException != null) if (ex.InnerException != null) Console.Error.WriteLine("Inner: " + ex.InnerException);
{
Console.Error.WriteLine("Inner: " + ex.InnerException.ToString());
}
Environment.Exit(3); Environment.Exit(3);
} }
@ -84,7 +82,8 @@ return;
static void PrintHelp() static void PrintHelp()
{ {
Console.WriteLine("XSDVisualiser - Parse an XSD and emit a structural model with types, constraints, and cardinality"); Console.WriteLine(
"XSDVisualiser - Parse an XSD and emit a structural model with types, constraints, and cardinality");
Console.WriteLine("Usage: XSDVisualiser <schema.xsd> [--format xml|json] [--out outputFile]"); Console.WriteLine("Usage: XSDVisualiser <schema.xsd> [--format xml|json] [--out outputFile]");
Console.WriteLine("Default format is xml; if --out is omitted the output is printed to stdout."); Console.WriteLine("Default format is xml; if --out is omitted the output is printed to stdout.");
} }

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types" <xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
xmlns:callctx="http://serviceplatformen.dk/xml/schemas/CallContext/1/" xmlns:callctx="http://serviceplatformen.dk/xml/schemas/CallContext/1/"
xmlns:authctx="http://serviceplatformen.dk/xml/schemas/AuthorityContext/1/" xmlns:authctx="http://serviceplatformen.dk/xml/schemas/AuthorityContext/1/"
xmlns:dsv2="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types" xmlns:dsv2="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
elementFormDefault="qualified" elementFormDefault="qualified"
version="1.0"> version="1.0">
@ -39,7 +39,8 @@
<xsd:complexType name="FordelingsobjektAfsendResponseType"> <xsd:complexType name="FordelingsobjektAfsendResponseType">
<xsd:sequence> <xsd:sequence>
<xsd:element name="ForretningsKvittering" type="dsv2:ForretningskvitteringType" minOccurs="1" maxOccurs="1"/> <xsd:element name="ForretningsKvittering" type="dsv2:ForretningskvitteringType" minOccurs="1"
maxOccurs="1"/>
<xsd:element name="DistributionContext" type="dsv2:DistributionContextType" minOccurs="1" maxOccurs="1"/> <xsd:element name="DistributionContext" type="dsv2:DistributionContextType" minOccurs="1" maxOccurs="1"/>
</xsd:sequence> </xsd:sequence>
</xsd:complexType> </xsd:complexType>
@ -48,7 +49,8 @@
<xsd:sequence> <xsd:sequence>
<xsd:element ref="callctx:CallContext" minOccurs="0" maxOccurs="1"/> <xsd:element ref="callctx:CallContext" minOccurs="0" maxOccurs="1"/>
<xsd:element ref="authctx:AuthorityContext" minOccurs="0" maxOccurs="1"/> <xsd:element ref="authctx:AuthorityContext" minOccurs="0" maxOccurs="1"/>
<xsd:element name="Forretningskvittering" type="dsv2:ForretningskvitteringType" minOccurs="1" maxOccurs="1"/> <xsd:element name="Forretningskvittering" type="dsv2:ForretningskvitteringType" minOccurs="1"
maxOccurs="1"/>
<xsd:element name="DistributionContext" type="dsv2:DistributionContextType" minOccurs="1" maxOccurs="1"/> <xsd:element name="DistributionContext" type="dsv2:DistributionContextType" minOccurs="1" maxOccurs="1"/>
</xsd:sequence> </xsd:sequence>
</xsd:complexType> </xsd:complexType>

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port" xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/"
name="DistributionServiceAnvender" name="DistributionServiceAnvender">
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">
<types> <types>
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"> <xsd:schema targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port" xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/"
name="DistributionServiceModtager" name="DistributionServiceModtager">
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">
<types> <types>
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"> <xsd:schema targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types" <xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
elementFormDefault="qualified" elementFormDefault="qualified"
version="1.0"> version="1.0">

View File

@ -1,15 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" <schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types" xmlns:oio="urn:oio:definitions:1.0.0"
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types" targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
elementFormDefault="qualified" elementFormDefault="qualified"
xmlns:oio="urn:oio:definitions:1.0.0"
oio:mapping="urn:oio:sagdok:MPD:3.0.0" oio:mapping="urn:oio:sagdok:MPD:3.0.0"
attributeFormDefault="unqualified"> attributeFormDefault="unqualified">
<simpleType name="URN"> <simpleType name="URN">
<restriction base="string"> <restriction base="string">
<pattern value="[uU][rR][nN]:[a-zA-Z0-9][a-zA-Z0-9-]{0,30}[a-zA-Z0-9]:[a-zA-Z0-9\(\)+,\\\-.:=@;$_!*'%/?#]+"/> <pattern
value="[uU][rR][nN]:[a-zA-Z0-9][a-zA-Z0-9-]{0,30}[a-zA-Z0-9]:[a-zA-Z0-9\(\)+,\\\-.:=@;$_!*'%/?#]+"/>
</restriction> </restriction>
</simpleType> </simpleType>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/" <xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
xmlns:tns="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
elementFormDefault="qualified" elementFormDefault="qualified"
version="1.0"> version="1.0">

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="ServiceplatformFaultMessage" <wsdl:definitions xmlns:tns="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
xmlns:tns="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
name="ServiceplatformFaultMessage"
targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/">
<wsdl:types> <wsdl:types>
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"> <xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/">

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/" <xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
elementFormDefault="qualified" elementFormDefault="qualified"
version="1.0"> version="1.0">

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="ServiceplatformFaultMessage" <wsdl:definitions xmlns:tns="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
xmlns:tns="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
name="ServiceplatformFaultMessage"
targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/">
<wsdl:types> <wsdl:types>
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/"> <xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/">

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port" xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/wsdl/"
name="DistributionService"
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types" xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
xmlns:wsp="http://www.w3.org/ns/ws-policy"> xmlns:wsp="http://www.w3.org/ns/ws-policy"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
xmlns="http://schemas.xmlsoap.org/wsdl/"
name="DistributionService">
<import namespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" location="policies.wsdl"/> <import namespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" location="policies.wsdl"/>
@ -73,7 +73,8 @@
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="FordelingsobjektAfsend"> <operation name="FordelingsobjektAfsend">
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsobjektAfsend"/> <soap:operation
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsobjektAfsend"/>
<input> <input>
<soap:body use="literal"/> <soap:body use="literal"/>
</input> </input>
@ -86,7 +87,8 @@
</operation> </operation>
<operation name="FordelingskvitteringModtag"> <operation name="FordelingskvitteringModtag">
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingskvitteringModtag"/> <soap:operation
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingskvitteringModtag"/>
<input> <input>
<soap:body use="literal"/> <soap:body use="literal"/>
</input> </input>
@ -114,7 +116,8 @@
</operation> </operation>
<operation name="FordelingsmodtagerValider"> <operation name="FordelingsmodtagerValider">
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsmodtagerValider"/> <soap:operation
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsmodtagerValider"/>
<input> <input>
<soap:body use="literal"/> <soap:body use="literal"/>
</input> </input>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<wsdl:definitions targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy">
<wsp:Policy Name="policies.wsdl#ServiceplatformBindingPolicy"> <wsp:Policy Name="policies.wsdl#ServiceplatformBindingPolicy">
<wsp:ExactlyOne> <wsp:ExactlyOne>

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port" xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/wsdl/"
name="DistributionService"
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types" xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
xmlns:wsp="http://www.w3.org/ns/ws-policy"> xmlns:wsp="http://www.w3.org/ns/ws-policy"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
xmlns="http://schemas.xmlsoap.org/wsdl/"
name="DistributionService">
<import namespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" location="policies.wsdl"/> <import namespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" location="policies.wsdl"/>
@ -73,7 +73,8 @@
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="FordelingsobjektAfsend"> <operation name="FordelingsobjektAfsend">
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsobjektAfsend"/> <soap:operation
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsobjektAfsend"/>
<input> <input>
<soap:body use="literal"/> <soap:body use="literal"/>
</input> </input>
@ -86,7 +87,8 @@
</operation> </operation>
<operation name="FordelingskvitteringModtag"> <operation name="FordelingskvitteringModtag">
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingskvitteringModtag"/> <soap:operation
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingskvitteringModtag"/>
<input> <input>
<soap:body use="literal"/> <soap:body use="literal"/>
</input> </input>
@ -114,7 +116,8 @@
</operation> </operation>
<operation name="FordelingsmodtagerValider"> <operation name="FordelingsmodtagerValider">
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsmodtagerValider"/> <soap:operation
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsmodtagerValider"/>
<input> <input>
<soap:body use="literal"/> <soap:body use="literal"/>
</input> </input>

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<wsdl:definitions targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy">
<wsp:Policy Name="policies.wsdl#ServiceplatformBindingPolicy"> <wsp:Policy Name="policies.wsdl#ServiceplatformBindingPolicy">
<wsp:ExactlyOne> <wsp:ExactlyOne>
@ -37,7 +37,8 @@
<wsp:Policy> <wsp:Policy>
<sp:InitiatorToken> <sp:InitiatorToken>
<wsp:Policy> <wsp:Policy>
<sp:SamlToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never"> <sp:SamlToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
<wsp:Policy> <wsp:Policy>
<sp:WssSamlV20Token11/> <sp:WssSamlV20Token11/>
</wsp:Policy> </wsp:Policy>
@ -46,7 +47,8 @@
</sp:InitiatorToken> </sp:InitiatorToken>
<sp:RecipientToken> <sp:RecipientToken>
<wsp:Policy> <wsp:Policy>
<sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator"> <sp:X509Token
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator">
<wsp:Policy> <wsp:Policy>
<sp:WssX509V3Token10/> <sp:WssX509V3Token10/>
</wsp:Policy> </wsp:Policy>
@ -70,7 +72,8 @@
</sp:AsymmetricBinding> </sp:AsymmetricBinding>
<sp:SignedSupportingTokens> <sp:SignedSupportingTokens>
<wsp:Policy> <wsp:Policy>
<sp:IssuedToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"> <sp:IssuedToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<sp:RequestSecurityTokenTemplate/> <sp:RequestSecurityTokenTemplate/>
<wsp:Policy> <wsp:Policy>
<sp:WssSamlV20Token11/> <sp:WssSamlV20Token11/>

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types" <xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
elementFormDefault="qualified" elementFormDefault="qualified"
version="1.0"> version="1.0">
<xsd:element name="TransportKvittering" type="tns:TransportkvitteringType" /> <xsd:element name="TransportKvittering" type="tns:TransportkvitteringType"/>
<xsd:simpleType name="TransportValideringsKodeType"> <xsd:simpleType name="TransportValideringsKodeType">
<xsd:restriction base="xsd:string"> <xsd:restriction base="xsd:string">
<xsd:enumeration value="OK"/> <xsd:enumeration value="OK"/>
@ -16,7 +16,8 @@
<xsd:complexType name="TransportkvitteringType"> <xsd:complexType name="TransportkvitteringType">
<xsd:sequence> <xsd:sequence>
<xsd:element name="TransportValideringsKode" type="tns:TransportValideringsKodeType" minOccurs="1" maxOccurs="1"/> <xsd:element name="TransportValideringsKode" type="tns:TransportValideringsKodeType" minOccurs="1"
maxOccurs="1"/>
<xsd:element name="Begrundelse" type="xsd:string" minOccurs="0" maxOccurs="1"/> <xsd:element name="Begrundelse" type="xsd:string" minOccurs="0" maxOccurs="1"/>
<xsd:element name="FejlListe" type="tns:FejlListeType" minOccurs="0" maxOccurs="1"/> <xsd:element name="FejlListe" type="tns:FejlListeType" minOccurs="0" maxOccurs="1"/>
</xsd:sequence> </xsd:sequence>
@ -55,7 +56,8 @@
<xsd:complexType name="ForretningskvitteringType"> <xsd:complexType name="ForretningskvitteringType">
<xsd:sequence> <xsd:sequence>
<xsd:element name="ForretningsValideringsKode" type="tns:ForretningsValideringsKodeType" minOccurs="1" maxOccurs="1"/> <xsd:element name="ForretningsValideringsKode" type="tns:ForretningsValideringsKodeType" minOccurs="1"
maxOccurs="1"/>
<xsd:element name="Kvitteringstype" type="tns:KvitteringstypeType" minOccurs="1" maxOccurs="1"/> <xsd:element name="Kvitteringstype" type="tns:KvitteringstypeType" minOccurs="1" maxOccurs="1"/>
<xsd:element name="Begrundelse" type="xsd:string" minOccurs="0" maxOccurs="1"/> <xsd:element name="Begrundelse" type="xsd:string" minOccurs="0" maxOccurs="1"/>
<xsd:element name="FejlListe" type="tns:FejlListeType" minOccurs="0" maxOccurs="1"/> <xsd:element name="FejlListe" type="tns:FejlListeType" minOccurs="0" maxOccurs="1"/>

View File

@ -1,17 +1,16 @@
using System;
using System.IO;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using System.Xml.Serialization; using System.Xml.Serialization;
namespace XSDVisualiser.Utils namespace XSDVisualiser.Utils;
public static class Serialization
{ {
public static class Serialization
{
private static readonly JsonSerializerOptions JsonOptions = new() private static readonly JsonSerializerOptions JsonOptions = new()
{ {
WriteIndented = true, WriteIndented = true,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
}; };
public static string ToXml<T>(T obj) public static string ToXml<T>(T obj)
@ -33,5 +32,4 @@ namespace XSDVisualiser.Utils
{ {
public override Encoding Encoding => Encoding.UTF8; public override Encoding Encoding => Encoding.UTF8;
} }
}
} }

View File

@ -6,11 +6,11 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Models\**\*.cs" /> <Compile Remove="Models\**\*.cs"/>
<Compile Remove="Parsing\**\*.cs" /> <Compile Remove="Parsing\**\*.cs"/>
<Compile Remove="Utils\**\*.cs" /> <Compile Remove="Utils\**\*.cs"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\XSDVisualiser.Core\XSDVisualiser.Core.csproj" /> <ProjectReference Include="..\XSDVisualiser.Core\XSDVisualiser.Core.csproj"/>
</ItemGroup> </ItemGroup>
</Project> </Project>