Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
public class XsdGenerator { private const string Xs = "http://www.w3.org/2001/XMLSchema"; public XDocument Generate(XmlValidator validator, string rootElementName) { var schema = new XElement(XName.Get("schema", Xs), new XAttribute(XNamespace.Xmlns + "xs", Xs)); var rootElement = new XElement(XName.Get("element", Xs), new XAttribute("name", rootElementName)); var rootComplex = new XElement(XName.Get("complexType", Xs)); var rootSeq = new XElement(XName.Get("sequence", Xs)); foreach (var rule in validator.Rules.OfType
()) { var segments = GetElementSegments(rootElementName, rule.Path); XElement parent = rootSeq; foreach (var seg in segments) { // Find or create element node var existing = parent.Elements(XName.Get("element", Xs)) .FirstOrDefault(e => (string)e.Attribute("name") == seg); if (existing == null) { existing = new XElement(XName.Get("element", Xs), new XAttribute("name", seg)); parent.Add(existing); var complex = new XElement(XName.Get("complexType", Xs)); var seq = new XElement(XName.Get("sequence", Xs)); complex.Add(seq); existing.Add(complex); } parent = existing.Element(XName.Get("complexType", Xs))! .Element(XName.Get("sequence", Xs))!; } // Apply restrictions/attributes at the deepest segment var targetElement = parent.Parent!.Parent!; // the
node ApplyRuleToElement(targetElement, rule); } var globalRules = validator.Rules.OfType
(); var annotation = GetAnnotation(rootElement); foreach (var gr in globalRules) { annotation.Add(new XElement(XName.Get("documentation", Xs), gr.Message ?? "Global validation rule applied")); } rootComplex.Add(rootSeq); rootElement.Add(rootComplex); schema.Add(rootElement); AddUniqueConstraints(new XDocument(schema), validator, rootElementName); return new XDocument(schema); } private XElement GetAnnotation(XElement rootElement) { var annotation = rootElement.Element(XName.Get("annotation", Xs)); if (annotation == null) { annotation = new XElement(XName.Get("annotation", Xs)); rootElement.Add(annotation); } return annotation; } private static string[] GetElementSegments(string root, string path) { // Trim leading/trailing slashes and split var segments = path.Trim('/').Split('/'); // Ensure the first segment matches root if (segments.Length == 0 || segments[0] != root) throw new InvalidOperationException($"Path '{path}' does not start with root '{root}'."); // Return all segments after root return segments.Skip(1).ToArray(); } private static void ApplyRuleToElement(XElement elementNode, XmlCompositeRule rule) { var complex = elementNode.Element(XName.Get("complexType", Xs)); var sequence = complex?.Element(XName.Get("sequence", Xs)); var attributes = new Dictionary
>(); var simpleContentNeeded = false; // Add empty annotation //// Collect annotation node once per element //var annotation = elementNode.Element(XName.Get("annotation", Xs)); //if (annotation == null) //{ // annotation = new XElement(XName.Get("annotation", Xs)); // elementNode.Add(annotation); //} foreach (var step in rule.RecordedSteps) { switch (step.Kind) { case XmlValidationRuleKind.AttributeRequired: case XmlValidationRuleKind.AttributeRegex: case XmlValidationRuleKind.AttributeRangeInt: case XmlValidationRuleKind.AttributeUnique: simpleContentNeeded = true; var name = step.AttributeName!; if (!attributes.ContainsKey(name)) attributes[name] = new List
(); attributes[name].Add(step); break; case XmlValidationRuleKind.ElementRequired: case XmlValidationRuleKind.ElementRegex: case XmlValidationRuleKind.ElementRangeInt: case XmlValidationRuleKind.ElementUnique: simpleContentNeeded = true; break; case XmlValidationRuleKind.CustomElement: //elementNode.Add(new XElement(XName.Get("annotation", Xs), // new XElement(XName.Get("documentation", Xs), // $"Custom rule applied: {step.Message}"))); break; case XmlValidationRuleKind.ElementAttributeCross: //elementNode.Add(new XElement(XName.Get("annotation", Xs), // new XElement(XName.Get("documentation", Xs), // $"Runtime rule on '{elementNode.Attribute("name")?.Value}': {step.Message ?? step.Kind.ToString()}"))); break; } } if (simpleContentNeeded || attributes.Count > 0) { var simpleContent = new XElement(XName.Get("simpleContent", Xs)); var extension = new XElement(XName.Get("extension", Xs), new XAttribute("base", InferBaseForElement(new[] { rule }))); foreach (var attr in attributes) { extension.Add(BuildAttribute(attr.Key, attr.Value)); } simpleContent.Add(extension); complex!.RemoveNodes(); complex.Add(simpleContent); } } private static string GetTopElementName(string root, string path) { // path like /order/id or /order/items/item var segments = path.Trim('/').Split('/'); if (segments.Length < 2 || segments[0] != root) return segments[0]; return segments[1]; } private static string? GetChildElementName(string root, string path) { var segments = path.Trim('/').Split('/'); if (segments.Length <= 2) return null; if (segments[0] != root) return null; return segments.Last(); } private static string InferMinOccurs(IEnumerable
rules) { var hasRequired = rules.Any(r => r.RecordedSteps.Any(s => s.Kind == XmlValidationRuleKind.ElementRequired)); return hasRequired ? "1" : "0"; } private static XName XS(string local) => XName.Get(local, Xs); private static XElement? BuildSimpleTypeForElement(IEnumerable
rules) { var regex = rules.SelectMany(r => r.RecordedSteps) .Where(s => s.Kind == XmlValidationRuleKind.ElementRegex) .Select(s => s.Arg1 as string) .FirstOrDefault(); var range = rules.SelectMany(r => r.RecordedSteps) .Where(s => s.Kind == XmlValidationRuleKind.ElementRangeInt) .Select(s => (Min: (int)s.Arg1!, Max: (int)s.Arg2!)) .FirstOrDefault(); if (regex == null && range.Min == 0 && range.Max == 0) return null; var simpleType = new XElement(XS("simpleType")); var restriction = new XElement(XS("restriction"), new XAttribute("base", range.Min != 0 || range.Max != 0 ? "xs:int" : "xs:string")); if (range.Min != 0 || range.Max != 0) { restriction.Add(new XElement(XS("minInclusive"), new XAttribute("value", range.Min))); restriction.Add(new XElement(XS("maxInclusive"), new XAttribute("value", range.Max))); } if (regex is not null) { restriction.Add(new XElement(XS("pattern"), new XAttribute("value", regex))); } simpleType.Add(restriction); return simpleType; } private static string InferBaseForElement(IEnumerable
rules) { var hasIntRange = rules.SelectMany(r => r.RecordedSteps) .Any(s => s.Kind == XmlValidationRuleKind.ElementRangeInt); return hasIntRange ? "xs:int" : "xs:string"; } private static XElement BuildAttribute(string name, IEnumerable
steps) { var attr = new XElement(XS("attribute"), new XAttribute("name", name)); var required = steps.Any(s => s.Kind == XmlValidationRuleKind.AttributeRequired); if (required) attr.Add(new XAttribute("use", "required")); var pattern = steps.Where(s => s.Kind == XmlValidationRuleKind.AttributeRegex).Select(s => s.Arg1 as string).FirstOrDefault(); var range = steps.Where(s => s.Kind == XmlValidationRuleKind.AttributeRangeInt).Select(s => (Min: (int)s.Arg1!, Max: (int)s.Arg2!)).FirstOrDefault(); if (pattern != null || (range.Min != 0 || range.Max != 0)) { var simpleType = new XElement(XS("simpleType")); var baseType = (range.Min != 0 || range.Max != 0) ? "xs:int" : "xs:string"; var restriction = new XElement(XS("restriction"), new XAttribute("base", baseType)); if (range.Min != 0 || range.Max != 0) { restriction.Add(new XElement(XS("minInclusive"), new XAttribute("value", range.Min))); restriction.Add(new XElement(XS("maxInclusive"), new XAttribute("value", range.Max))); } if (pattern != null) { restriction.Add(new XElement(XS("pattern"), new XAttribute("value", pattern))); } simpleType.Add(restriction); attr.Add(simpleType); } return attr; } private static void AddUniqueConstraints(XDocument schema, XmlValidator validator, string rootElementName) { // Element-level unique constraints var elementUniqueRules = validator.Rules.OfType
() .Where(r => r.RecordedSteps.Any(s => s.Kind == XmlValidationRuleKind.ElementUnique)); foreach (var rule in elementUniqueRules) { schema.Root!.Add(new XElement(XName.Get("unique", Xs), new XAttribute("name", $"unique_{San(rule.Path)}"), new XElement(XName.Get("selector", Xs), new XAttribute("xpath", rule.Path)), new XElement(XName.Get("field", Xs), new XAttribute("xpath", ".")))); } // Attribute-level unique constraints var attributeUniqueRules = validator.Rules.OfType
() .SelectMany(r => r.RecordedSteps.Select(s => (Rule: r, Step: s))) .Where(rs => rs.Step.Kind == XmlValidationRuleKind.AttributeUnique); foreach (var (r, step) in attributeUniqueRules) { schema.Root!.Add(new XElement(XName.Get("unique", Xs), new XAttribute("name", $"unique{San(r.Path)}{step.AttributeName}"), new XElement(XName.Get("selector", Xs), new XAttribute("xpath", r.Path)), new XElement(XName.Get("field", Xs), new XAttribute("xpath", $"@{step.AttributeName}")))); } static string San(string s) => s.Trim('/').Replace("/", "").Replace("@", ""); } }
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)