2012-07-19 6 views
8

Ich habe eine XSD mit mehreren komplexen Typen und einfachen Typen (Teil der Datei unten gezeigt). Ich muss dieses Dokument analysieren, um maxLength von jedem der SimpleTypes zu erhalten, auf die in den komplexen Typen verwiesen wird. Kann jemand bitte einen Ratschlag geben, wie man das umsetzt? Ich muss dies in einer generischen Weise implementieren, wenn ich nach "Setup_Type" frage, sollte es die folgende Ausgabe geben. Vielen Dank!Wie analysiert man eine XSD, um die Informationen von <xsd: simpleType> -Elementen mit C# zu erhalten?

NewSetup/Amount = 12 (Der Name-Attribute von Element-Tags durch "/" getrennt und maxLength aus dem verschachtelten Simple)

NewSetup/Name = 50

<xsd:complexType name="Setup_Type"> 
    <xsd:sequence> 
    <xsd:element name="NewSetup" type="NewSetup_Type" minOccurs="1" maxOccurs="1" /> 
    </xsd:sequence> 
</xsd:complexType> 

<xsd:complexType name="NewSetup_Type"> 
    <xsd:sequence> 
    <xsd:element name="Amount" type="Amount_Type" minOccurs="1" maxOccurs="1" /> 
    <xsd:element name="Name" type="Name_Type" minOccurs="1" maxOccurs="1" /> 
    </xsd:sequence> 
</xsd:complexType> 

<xsd:simpleType name="Amount_Type"> 
    <xsd:annotation> 
    <xsd:documentation>Amount</xsd:documentation> 
    </xsd:annotation> 
    <xsd:restriction base="xsd:string"> 
    <xsd:maxLength value="12" /> 
    </xsd:restriction> 
</xsd:simpleType> 

<xsd:simpleType name="Name_Type"> 
    <xsd:annotation> 
    <xsd:documentation>Name</xsd:documentation> 
    </xsd:annotation> 
    <xsd:restriction base="xsd:string"> 
    <xsd:maxLength value="50" /> 
    </xsd:restriction> 
</xsd:simpleType> 
+0

Vielen Dank für die tollen Lösungen. Es war sehr hilfreich, sich den Code anzuschauen. Bis jetzt habe ich @ psubse2003 Lösung versucht und es funktioniert gut für mich. – Jyina

Antwort

17

Ich habe in der Vergangenheit gefragt ähnliche Fragen gesehen (vollständige Offenlegung, frage ich habe ein ähnliches question mich). Das Parsen einer XSD ist nichts für schwache Nerven.

Sie haben grundsätzlich 2 Optionen, erstens ist einfacher zu implementieren, aber kann leichter durch kleinere Änderungen an der XSD gebrochen werden. Der 2. ist robuster, aber schwer zu implementieren.

Option 1:

Parsen der XSD mit LINQ (oder einem anderen C# XML-Parser, wenn Sie bevorzugen). Da ein XSD nur ein XML ist, können Sie es in ein XDocument laden und nur über LINQ lesen.

Für nur eine Probe Ihres eigenen XSD:

<xsd:simpleType name="Amount_Type"> 
    <xsd:annotation> 
    <xsd:documentation>Amount</xsd:documentation> 
    </xsd:annotation> 
    <xsd:restriction base="xsd:string"> 
    <xsd:maxLength value="12" /> 
    </xsd:restriction> 
</xsd:simpleType> 

Sie die MaxLength zugreifen:

var xDoc = XDocument.Load("your XSD path"); 
var ns = XNamespace.Get(@"http://www.w3.org/2001/XMLSchema"); 

var length = (from sType in xDoc.Element(ns + "schema").Elements(ns + "simpleType") 
       where sType.Attribute("name").Value == "Amount_Type" 
       from r in sType.Elements(ns + "restriction") 
       select r.Element(ns + "maxLength").Attribute("value") 
         .Value).FirstOrDefault(); 

Das ist nicht eine sehr einfache Methode für die Analyse von Typnamen bietet, insbesondere für längere Arten. Um dies zu verwenden, müssen Sie den genauen Pfad für jedes Element kennen, nach dem Sie suchen.

Option 2:

Das ist viel zu komplex für eine schnelle Antwort (Anmerkung: siehe bearbeiten unten - ich einige Zeit hatte und zusammen eine Arbeitslösung), so werde ich ermutigen, Sie schauen sich meine eigene Frage an, die ich oben verlinkt habe. Darin verknüpfte ich eine great blog, die zeigt, wie Sie die XSD ernsthaft in Teile zerlegen und möglicherweise ermöglichen Sie die Art der Suche, die Sie wollen. Sie müssen entscheiden, ob es sich lohnt, sie zu entwickeln (das Blog zeigt eine Implementierung mit XmlReader, die ein XML enthält, das gegen die betreffende XSD validiert wird, aber Sie können dies einfach durch direktes Laden der XSD und Parsing erreichen.

2 Schlüsselidee im Blog zu finden sind:

// in the getRestriction method (reader in this context is an `XmlReader` that 
// contains a XML that is being validated against the specific XSD 
if (reader.SchemaInfo.SchemaElement == null) return null; 
simpleType = reader.SchemaInfo.SchemaElement.ElementSchemaType as XmlSchemaSimpleType; 
if (simpleType == null) return null; 
restriction = simpleType.Content as XmlSchemaSimpleTypeRestriction; 

// then in the getMaxLength method 
if (restriction == null) return null; 
List<int> result = new List<int>(); 
foreach (XmlSchemaObject facet in restriction.Facets) { 
if (facet is XmlSchemaMaxLengthFacet) result.Add(int.Parse(((XmlSchemaFacet) facet).Value)); 

Ich habe versucht, tatsächlich das gleiche letztes Jahr ein XSD als Teil einer komplizierten Datenvalidierungsmethode zu analysieren. Ich brauchte den größten Teil einer Woche, um wirklich zu verstehen, was passierte, um die Methoden im Blog an meine Zwecke anzupassen. Es ist definitiv der beste Weg, genau das zu implementieren, was Sie wollen.

Wenn Sie dies mit einem eigenständigen Schema versuchen möchten, können Sie die XSD in ein XmlSchemaSet Objekt laden und dann die GlobalTypes-Eigenschaft verwenden, um den gesuchten Typ zu finden.


EDIT: zog ich meinen alten Code auf und begann den Code zusammenzustellen, Ihnen zu helfen.

XmlSchemaSet set; // this needs to be accessible to the methods below, 
        // so should be a class level field or property 

using (var fs = new FileStream(@"your path here", FileMode.Open) 
{ 
    var schema = XmlSchema.Read(fs, null); 

    set = new XmlSchemaSet(); 
    set.Add(schema); 
    set.Compile(); 
} 

Die folgenden Methoden geben, sollten Sie in der Nähe zu dem, was Sie wollen auf der Grundlage der XSD Sie bereitgestellt:

Erstes Ihr Schema laden. Es sollte ziemlich anpassungsfähig sein, um mit komplexeren Strukturen umzugehen.

public Dictionary<string, int> GetElementMaxLength(String xsdElementName) 
{ 
    if (xsdElementName == null) throw new ArgumentException(); 
    // if your XSD has a target namespace, you need to replace null with the namespace name 
    var qname = new XmlQualifiedName(xsdElementName, null); 

    // find the type you want in the XmlSchemaSet  
    var parentType = set.GlobalTypes[qname]; 

    // call GetAllMaxLength with the parentType as parameter 
    var results = GetAllMaxLength(parentType); 

    return results; 
} 

private Dictionary<string, int> GetAllMaxLength(XmlSchemaObject obj) 
{ 
    Dictionary<string, int> dict = new Dictionary<string, int>(); 

    // do some type checking on the XmlSchemaObject 
    if (obj is XmlSchemaSimpleType) 
    { 
     // if it is a simple type, then call GetMaxLength to get the MaxLength restriction 
     var st = obj as XmlSchemaSimpleType; 
     dict[st.QualifiedName.Name] = GetMaxLength(st); 
    } 
    else if (obj is XmlSchemaComplexType) 
    { 

     // if obj is a complexType, cast the particle type to a sequence 
     // and iterate the sequence 
     // warning - this will fail if it is not a sequence, so you might need 
     // to make some adjustments if you have something other than a xs:sequence 
     var ct = obj as XmlSchemaComplexType; 
     var seq = ct.ContentTypeParticle as XmlSchemaSequence; 

     foreach (var item in seq.Items) 
     { 
      // item will be an XmlSchemaObject, so just call this same method 
      // with item as the parameter to parse it out 
      var rng = GetAllMaxLength(item); 

      // add the results to the dictionary 
      foreach (var kvp in rng) 
      { 
       dict[kvp.Key] = kvp.Value; 
      } 
     } 
    } 
    else if (obj is XmlSchemaElement) 
    { 
     // if obj is an XmlSchemaElement, the you need to find the type 
     // based on the SchemaTypeName property. This is why your 
     // XmlSchemaSet needs to have class-level scope 
     var ele = obj as XmlSchemaElement; 
     var type = set.GlobalTypes[ele.SchemaTypeName]; 

     // once you have the type, call this method again and get the dictionary result 
     var rng = GetAllMaxLength(type); 

     // put the results in this dictionary. The difference here is the dictionary 
     // key is put in the format you specified 
     foreach (var kvp in rng) 
     { 
      dict[String.Format("{0}/{1}", ele.QualifiedName.Name, kvp.Key)] = kvp.Value; 
     } 
    } 

    return dict; 
} 

private Int32 GetMaxLength(XmlSchemaSimpleType xsdSimpleType) 
{ 
    // get the content of the simple type 
    var restriction = xsdSimpleType.Content as XmlSchemaSimpleTypeRestriction; 

    // if it is null, then there are no restrictions and return -1 as a marker value 
    if (restriction == null) return -1; 

    Int32 result = -1; 

    // iterate the facets in the restrictions, look for a MaxLengthFacet and parse the value 
    foreach (XmlSchemaObject facet in restriction.Facets) 
    { 
     if (facet is XmlSchemaMaxLengthFacet) 
     { 
      result = int.Parse(((XmlSchemaFacet)facet).Value); 
      break; 
     } 
    } 

    return result; 
} 

Dann wird die Nutzung ist ziemlich einfach, Sie müssen nur die GetElementMaxLength(String) Methode aufrufen und es wird ein Wörterbuch der Namen in dem Format, das Sie mit dem Wert als die maximale Länge versehen zurück:

var results = GetElementMaxLength("Setup_Type"); 

foreach (var item in results) 
{ 
    Console.WriteLine("{0} | {1}", item.Key, item.Value);     
} 
+0

Ihre Lösung funktioniert gut für mich und genau für meine Anforderungen. Ich bin auch in der Lage, die anderen Informationen wie Datentyp, Muster aus dem Element des SimpleType wie folgt zu erhalten. – Jyina

+0

@Jyina Sie können alle Arten von guten Informationen von der XSD finden, sobald Sie die richtigen Eigenschaften zu sehen und die richtige Besetzung zu machen. Die Guss- und Typprüfung ist der schwierigste Teil, um herauszufinden. – psubsee2003

+0

Vielen Dank für Ihre Hilfe und dafür, dass Sie sich die Zeit genommen haben, mir den Code zu zeigen. – Jyina

0

Meine Lösung möglicherweise nicht genau Wonach suchen Sie. Wahrscheinlich würden Sie lieber System.Xml-Klassen verwenden, um solche Informationen zu verarbeiten. Ich weiß nicht, wie viel Generika dieser Parser sein soll, jedenfalls sind das nur meine 2 Cent. Mein Code verwendet nur reguläre Ausdrücke, die so entworfen wurden, dass sie 99% der Möglichkeiten korrekt darstellen (ich schätze). Jemand würde das wie eine Fliege mit einer Pistole schießen nennen. Wie auch immer, das ist es:

using System.Text.RegularExpressions; 
using System.IO; 

static class Program 
{ 
    static void main() 
    { 
     XsdFile file = new XsdFile(@"c:\temp\test.xsd"); 
     Console.WriteLine(file.Query("Setup_Type")); 
    } 
} 

public class XsdFile 
{ 

    Dictionary<string, XsdType> types; 

    public XsdFile(string path) 
    { 
     string xsdBody = File.ReadAllText(path); 
     types = XsdType.CreateTypes(xsdBody); 
    } 

    public string Query(string typename) { 
     return Query(typename, ""); 
    } 

    private string Query(string typename, string parent) 
    { 
     XsdType type; 
     if (types.TryGetValue(typename, out type)) 
     { 
      if (type.GetType() == typeof(ComplexType)) 
      { 
       StringBuilder sb = new StringBuilder(); 
       ComplexType complexType = (ComplexType)type; 
       foreach (string elementName in complexType.elements.Keys) 
       { 
        string elementType = complexType.elements[elementName]; 
        sb.AppendLine(Query(elementType, parent + "/" + elementName)); 
       } 
       return sb.ToString(); 
      } 
      else if (type.GetType() == typeof(SimpleType)) 
      { 
       SimpleType simpleType = (SimpleType)type; 
       return string.Format("{0} = {1}", parent, simpleType.maxLength); 
      } 
      else { 
       return ""; 
      } 
     } 
     else 
     { 
      return ""; 
     } 
    } 
} 

public abstract class XsdType 
{ 

    string name; 

    public XsdType(string name) 
    { 
     this.name = name; 
    } 

    public static Dictionary<string, XsdType> CreateTypes(string xsdBody) 
    { 

     Dictionary<string, XsdType> types = new Dictionary<string, XsdType>(); 

     MatchCollection mc_types = Regex.Matches(xsdBody, @"<xsd:(?<kind>complex|simple)Type[\s\t]+(?<attributes>[^>]+)>(?<body>.+?)</xsd:\1Type>", RegexOptions.Singleline); 
     foreach (Match m_type in mc_types) 
     { 
      string typeKind = m_type.Groups["kind"].Value; 
      string typeAttributes = m_type.Groups["attributes"].Value; 
      string typeBody = m_type.Groups["body"].Value; 
      string typeName; 
      Match m_nameattribute = Regex.Match(typeAttributes, @"name[\s\t]*=[\s\t]*""(?<name>[^""]+)""", RegexOptions.Singleline); 
      if (m_nameattribute.Success) 
      { 
       typeName = m_nameattribute.Groups["name"].Value; 
       if (typeKind == "complex") 
       { 
        ComplexType current_type = new ComplexType(typeName); 
        MatchCollection mc_elements = Regex.Matches(typeBody, @"<xsd:element(?<attributes>.+?)/>", RegexOptions.Singleline); 
        foreach (Match m_element in mc_elements) 
        { 
         Dictionary<string, string> elementAttributes = ParseAttributes(m_element.Groups["attributes"].Value); 
         string elementName; 
         string elementType; 
         if (!elementAttributes.TryGetValue("name", out elementName)) 
          continue; 
         if (!elementAttributes.TryGetValue("type", out elementType)) 
          continue; 
         current_type.elements.Add(elementName, elementType); 
        } 
        types.Add(current_type.name, current_type); 
       } 
       else if (typeKind == "simple") 
       { 
        Match m_maxLength = Regex.Match(typeBody, @"<xsd:restriction[^>]+>.+?<xsd:maxLength.+?value=""(?<maxLength>[^""]+)""", RegexOptions.Singleline); 
        if (m_maxLength.Success) 
        { 
         string maxLength = m_maxLength.Groups["maxLength"].Value; 
         SimpleType current_type = new SimpleType(typeName); 
         current_type.maxLength = maxLength; 
         types.Add(current_type.name, current_type); 
        } 
       } 
      } 
      else 
      { 
       continue; 
      } 
     } 
     return types; 
    } 

    private static Dictionary<string, string> ParseAttributes(string value) 
    { 
     Dictionary<string, string> attributes = new Dictionary<string, string>(); 
     MatchCollection mc_attributes = Regex.Matches(value, @"(?<name>[^=\s\t]+)[\s\t]*=[\s\t]*""(?<value>[^""]+)""", RegexOptions.Singleline); 
     foreach (Match m_attribute in mc_attributes) 
     { 
      attributes.Add(m_attribute.Groups["name"].Value, m_attribute.Groups["value"].Value); 
     } 
     return attributes; 
    } 

} 

public class SimpleType : XsdType 
{ 

    public string maxLength; 

    public SimpleType(string name) 
     : base(name) 
    { 
    } 

} 

public class ComplexType : XsdType 
{ 

    //(name-type) 
    public Dictionary<string, string> elements = new Dictionary<string,string>(); 

    public ComplexType(string name) 
     : base(name) 
    { 
    } 

} 
+1

Ich glaube nicht, Regex eignet sich für Pars XML (XSD in diesem Fall). Angenommen, ich entscheide mich, meinen XSD-Namespace 'xs' anstelle von' xsd' ... – polkduran

+1

-1 für die Verwendung von Regexen zu verwenden, um XML zu parsen, wenn bereits in .NET integrierte Bibliotheken dasselbe und noch besser machen. – siride

1
public class result_tree 
{ 
    public string nodevalue = ""; 

    public bool IsTerminal { get { return ChildCount == 0; } } 

    public List<result_tree> children = new List<result_tree>(); 

    public int ChildCount { get { return children.Count; } } 

    public result_tree(string v) { nodevalue = v; } 

    private void print_children(bool skip, string prefix) 
    { 
     if (IsTerminal) 
      Console.WriteLine(prefix + (prefix.Length==0?"":"/") + nodevalue); 
     else 
      foreach (result_tree rt in children) 
       rt.print_children(false,prefix + (prefix.Length == 0 ? "" : "/") + (skip?"":nodevalue)); 
    } 

    public void print_children() 
    { 
     print_children(true,""); 
    } 
} 

static class Program 
{ 
    private static void ValidationCallBack(object sender, ValidationEventArgs args) 
    { 
     Console.WriteLine(args.Message); 
    } 

    public static result_tree results; 



    static string deref_simple(XmlSchemaSimpleType simp) 
    { 
     XmlSchemaSimpleTypeRestriction xsstr = (XmlSchemaSimpleTypeRestriction)simp.Content; 
     foreach (object o in xsstr.Facets) 
     { 
      if (o.GetType() == typeof(XmlSchemaMaxLengthFacet)) 
      { 
       XmlSchemaMaxLengthFacet fac = (XmlSchemaMaxLengthFacet)o; 
       return fac.Value; 
      } 
     } 
     return ""; 
    } 

    static result_tree deref_complex(XmlSchema xs, XmlSchemaComplexType cplx) 
    { 
     result_tree rt = new result_tree(cplx.Name); 

     if (cplx.Particle.GetType() == typeof(XmlSchemaSequence)) 
     { 
      XmlSchemaSequence seq = (XmlSchemaSequence)cplx.Particle; 
      foreach (object o in seq.Items) 
      { 
       if (o.GetType() == typeof(XmlSchemaElement)) 
       { 
        XmlSchemaElement elem = (XmlSchemaElement)o; 

        XmlQualifiedName name = elem.SchemaTypeName; 

        result_tree branch; 

        object referto = xs.SchemaTypes[name]; 
        if (referto.GetType() == typeof(XmlSchemaComplexType)) 
        { 
         branch = deref_complex(xs,(XmlSchemaComplexType)referto); 
         branch.nodevalue = elem.Name; 
        } 
        else if (referto.GetType() == typeof(XmlSchemaSimpleType)) 
        { 
         XmlSchemaSimpleType st = (XmlSchemaSimpleType)referto; 

         branch = new result_tree(elem.Name + " = " + deref_simple(st).ToString()); 
        } 
        else 
        { 
         branch = null; 
        } 
        if(branch != null) 
         rt.children.Add(branch); 

       } 
      } 
     } 

     return rt; 
    } 

    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static void Main() 
    { 

     StreamReader sr = new StreamReader("aschema.xml"); 
     XmlSchema xs = XmlSchema.Read(sr, ValidationCallBack); 
     XmlSchemaSet xss = new XmlSchemaSet(); 
     xss.Add(xs); 
     xss.Compile(); 

     Console.WriteLine("Query: "); 
     string q = Console.ReadLine(); 

     XmlQualifiedName xqn = new XmlQualifiedName(q); 

     if (xs.SchemaTypes.Contains(xqn)) 
     { 
      object o = xs.SchemaTypes[xqn]; 
      if (o.GetType() == typeof(XmlSchemaComplexType)) 
      { 
       results = deref_complex(xs, (XmlSchemaComplexType)o); 
       results.print_children(); 
      } 
     } 
     else 
     { 
      Console.WriteLine("Not found!"); 
     } 

    } 
} 
+0

Ich nehme an, die Importe wären auch praktisch: using System; mit System.Collections.Generic; mit System.Linq; mit System.Windows.Forms; mit System.IO; mit System.Xml; mit System.Xml.Schema; mit System.Collections; –