Added simple export functionality
This commit is contained in:
parent
e56b76f3de
commit
589495911d
@ -39,6 +39,7 @@
|
|||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<MenuItem Header="Copy as TSV" Click="OnCopyAsTsvClick" />
|
<MenuItem Header="Copy as TSV" Click="OnCopyAsTsvClick" />
|
||||||
<MenuItem Header="Validate XML against this element…" Click="OnValidateAgainstXmlClick" />
|
<MenuItem Header="Validate XML against this element…" Click="OnValidateAgainstXmlClick" />
|
||||||
|
<MenuItem Header="Export subtree as XML…" Click="OnExportXmlClick" />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</Border.ContextMenu>
|
</Border.ContextMenu>
|
||||||
<StackPanel Orientation="Vertical" Spacing="2">
|
<StackPanel Orientation="Vertical" Spacing="2">
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
@ -251,4 +254,132 @@ public partial class LeftTreeView : UserControl
|
|||||||
|
|
||||||
return string.Join(";", parts);
|
return string.Join(";", parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void OnExportXmlClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Determine the node from the menu item's DataContext
|
||||||
|
var node = (sender as MenuItem)?.DataContext as SchemaNode ?? (sender as Control)?.GetVisualAncestors().OfType<Control>().FirstOrDefault()?.DataContext as SchemaNode;
|
||||||
|
if (node == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var top = TopLevel.GetTopLevel(this);
|
||||||
|
var sp = top?.StorageProvider;
|
||||||
|
if (sp == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var suggestedName = string.IsNullOrWhiteSpace(node.Name) ? "schema-node.xml" : $"{node.Name}.xml";
|
||||||
|
var saveOptions = new FilePickerSaveOptions
|
||||||
|
{
|
||||||
|
Title = "Export subtree as XML",
|
||||||
|
SuggestedFileName = suggestedName,
|
||||||
|
DefaultExtension = "xml",
|
||||||
|
ShowOverwritePrompt = true,
|
||||||
|
FileTypeChoices = new List<FilePickerFileType>
|
||||||
|
{
|
||||||
|
new FilePickerFileType("XML file") { Patterns = new List<string> { "*.xml" }, MimeTypes = new List<string>{"application/xml","text/xml"} },
|
||||||
|
FilePickerFileTypes.All
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var dest = await sp.SaveFilePickerAsync(saveOptions);
|
||||||
|
if (dest == null) return;
|
||||||
|
|
||||||
|
var localPath = dest.TryGetLocalPath();
|
||||||
|
if (string.IsNullOrEmpty(localPath))
|
||||||
|
{
|
||||||
|
await ShowTextDialogAsync("Export XML", "The selected destination is not accessible as a local path.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var xml = BuildXmlString(node);
|
||||||
|
await File.WriteAllTextAsync(localPath!, xml, Encoding.UTF8);
|
||||||
|
await ShowTextDialogAsync("Export XML", $"Exported subtree to:\n{localPath}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await ShowTextDialogAsync("Export XML", $"Failed to export XML.\n\n{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildXmlString(SchemaNode root)
|
||||||
|
{
|
||||||
|
var settings = new XmlWriterSettings
|
||||||
|
{
|
||||||
|
Indent = true,
|
||||||
|
OmitXmlDeclaration = false,
|
||||||
|
Encoding = Encoding.UTF8,
|
||||||
|
NewLineOnAttributes = false
|
||||||
|
};
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
using (var writer = XmlWriter.Create(sb, settings))
|
||||||
|
{
|
||||||
|
writer.WriteStartDocument();
|
||||||
|
WriteElementRecursive(writer, root);
|
||||||
|
writer.WriteEndDocument();
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteElementRecursive(XmlWriter writer, SchemaNode node)
|
||||||
|
{
|
||||||
|
var localName = string.IsNullOrWhiteSpace(node.Name) ? "Element" : node.Name!;
|
||||||
|
if (!string.IsNullOrEmpty(node.Namespace))
|
||||||
|
writer.WriteStartElement(localName, node.Namespace);
|
||||||
|
else
|
||||||
|
writer.WriteStartElement(localName);
|
||||||
|
|
||||||
|
// Write attributes if any (use type + constraints as placeholder value)
|
||||||
|
foreach (var attr in node.Attributes)
|
||||||
|
{
|
||||||
|
var attrName = string.IsNullOrWhiteSpace(attr.Name) ? "attr" : attr.Name!;
|
||||||
|
var value = BuildTypeAndConstraintText(attr.BuiltInType ?? attr.TypeName, attr.Constraints);
|
||||||
|
if (!string.IsNullOrEmpty(attr.Namespace))
|
||||||
|
writer.WriteAttributeString(attrName, attr.Namespace, value);
|
||||||
|
else
|
||||||
|
writer.WriteAttributeString(attrName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasChildren = node.Children is { Count: > 0 };
|
||||||
|
if (hasChildren)
|
||||||
|
{
|
||||||
|
foreach (var child in node.Children)
|
||||||
|
{
|
||||||
|
WriteElementRecursive(writer, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// leaf node: place datatype and constraints in text content
|
||||||
|
var text = BuildTypeAndConstraintText(node.BuiltInType ?? node.TypeName, node.Constraints);
|
||||||
|
if (!string.IsNullOrWhiteSpace(text))
|
||||||
|
writer.WriteString(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildTypeAndConstraintText(string? type, ConstraintSet? constraintSet)
|
||||||
|
{
|
||||||
|
var cons = constraintSet?.Constraints;
|
||||||
|
var typePart = string.IsNullOrWhiteSpace(type) ? string.Empty : type.Trim();
|
||||||
|
|
||||||
|
var constraintsPart = string.Empty;
|
||||||
|
if (cons is { Count: > 0 })
|
||||||
|
{
|
||||||
|
var tmpNode = new SchemaNode { Constraints = constraintSet };
|
||||||
|
constraintsPart = FormatConstraints(tmpNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(typePart) && string.IsNullOrEmpty(constraintsPart))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(constraintsPart))
|
||||||
|
return typePart;
|
||||||
|
return string.IsNullOrEmpty(typePart) ? constraintsPart :
|
||||||
|
$"{typePart} {constraintsPart}";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user