diff --git a/XSDVisualiser.Desktop/Views/RightDetailsView.axaml b/XSDVisualiser.Desktop/Views/RightDetailsView.axaml index 49891e3..98faab9 100644 --- a/XSDVisualiser.Desktop/Views/RightDetailsView.axaml +++ b/XSDVisualiser.Desktop/Views/RightDetailsView.axaml @@ -39,6 +39,14 @@ + + + + + + + + diff --git a/XSDVisualiser/Models/XsdSchemaModel.cs b/XSDVisualiser/Models/XsdSchemaModel.cs index bfd4cb7..bb1c322 100644 --- a/XSDVisualiser/Models/XsdSchemaModel.cs +++ b/XSDVisualiser/Models/XsdSchemaModel.cs @@ -55,6 +55,13 @@ namespace XSDVisualiser.Models [XmlAttribute] public string? ContentModel { get; set; } // sequence | choice | all | simple + /// + /// Human-readable documentation extracted from xsd:annotation/xsd:documentation. + /// Prefer element-level documentation; falls back to type-level documentation. + /// + [XmlElement] + public string? Documentation { get; set; } + public override string ToString() { // Used by AutoCompleteBox to get text for filtering and matching diff --git a/XSDVisualiser/Parsing/XsdSchemaParser.cs b/XSDVisualiser/Parsing/XsdSchemaParser.cs index 9e69023..a2c9a31 100644 --- a/XSDVisualiser/Parsing/XsdSchemaParser.cs +++ b/XSDVisualiser/Parsing/XsdSchemaParser.cs @@ -1,5 +1,6 @@ using System.Xml; using System.Xml.Schema; +using System.Text; using XSDVisualiser.Models; namespace XSDVisualiser.Core @@ -54,6 +55,9 @@ namespace XSDVisualiser.Core ContentModel = parentContentModel }; + // Prefer element-level documentation + node.Documentation = ExtractDocumentation(element); + var type = ResolveElementType(element); if (type == null) return node; node.TypeName = GetQualifiedTypeName(type); @@ -62,6 +66,20 @@ namespace XSDVisualiser.Core node.BuiltInType = type.Datatype.TypeCode.ToString(); } + // Fallback to type-level documentation if none on element + if (string.IsNullOrWhiteSpace(node.Documentation)) + { + switch (type) + { + case XmlSchemaComplexType ctDoc: + node.Documentation = ExtractDocumentation(ctDoc); + break; + case XmlSchemaSimpleType stDoc: + node.Documentation = ExtractDocumentation(stDoc); + break; + } + } + switch (type) { case XmlSchemaComplexType ct: @@ -413,5 +431,46 @@ namespace XSDVisualiser.Core } } } + private static string? ExtractDocumentation(XmlSchemaAnnotated? annotated) + { + if (annotated?.Annotation == null) return null; + var sb = new StringBuilder(); + foreach (var item in annotated.Annotation.Items) + { + if (item is not XmlSchemaDocumentation doc) continue; + + if (doc.Markup is { Length: > 0 }) + { + var pieceBuilder = new StringBuilder(); + foreach (var node in doc.Markup) + { + try + { + var text = node?.InnerText; + if (!string.IsNullOrWhiteSpace(text)) + { + pieceBuilder.Append(text); + } + } + catch + { + // ignore malformed nodes + } + } + + var piece = pieceBuilder.ToString().Trim(); + if (string.IsNullOrWhiteSpace(piece)) continue; + + if (sb.Length > 0) sb.AppendLine().AppendLine(); + sb.Append(piece); + } + else if (!string.IsNullOrWhiteSpace(doc.Source)) + { + // If there is a source but no markup, skip; we only render text. + } + } + + return sb.Length == 0 ? null : sb.ToString(); + } } }