2012-07-19 10 views
8

Mam XSD z wieloma typami złożonymi i typami prostymi (część pliku pokazana poniżej). Muszę parsować ten dokument, aby uzyskać maxLength z każdego z typów sim, które są odniesione w złożonych typach. Czy ktoś może podać kilka porad, jak to wdrożyć? Muszę zaimplementować to w sposób ogólny, więc jeśli wyślę zapytanie "Setup_Type", powinien podać poniższe dane wyjściowe. Dziękuję Ci!Jak przeanalizować XSD, aby uzyskać informacje z <xsd: simpleType> elementów przy użyciu C#?

NewSetup/wartości = 12 (Nazwa atrybuty znaczniki elementów oddzielonych "/" MAXLENGTH z zagnieżdżonej simpletype)

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

Dziękuję wszystkim za wspaniałe rozwiązania. Bardzo pomocne było spojrzenie na kod. Do tej pory próbowałem rozwiązania @ psubsee2003 i działa to doskonale dla mnie. – Jyina

Odpowiedz

17

Widziałem podobne pytania zadawane w przeszłości (pełne ujawnienie, mam poprosić podobny question siebie). Parsowanie XSD nie jest dla osób o słabym sercu.

Zasadniczo masz dwie opcje, łatwiejsze do wdrożenia, ale łatwiejsze do złamania poprzez niewielkie zmiany w XSD. druga jest bardziej rozbudowana, ale trudna do wdrożenia.

Wariant 1:

Przetwarzanie XSD z LINQ (C# lub innego parsera XML, jeśli wolisz). Ponieważ XSD to tylko XML, możesz załadować go do XDocument i po prostu odczytać go przez LINQ.

uzyskać tylko próbka własnego 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> 

można uzyskać dostęp do MaxLength:

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(); 

nie oferują bardzo łatwy sposób analizowania wg nazwy typu, zwłaszcza przez dłuższy typy. Aby tego użyć, musisz znać dokładną ścieżkę dla każdego poszukiwanego elementu.

Opcja 2:

To jest zbyt skomplikowane dla szybkiej odpowiedzi (uwaga: patrz Edycja - Miałem trochę czasu i ułożyła roztwór roboczy), więc mam zamiar zachęcić możesz spojrzeć na moje własne pytanie, które podałem powyżej. W nim podłączyłem great blog, który pokazuje, jak poważnie zepsuć XSD na kawałki i może pozwolić ci na wykonanie pożądanego rodzaju wyszukiwania. Musisz zdecydować, czy warto ją rozwijać (blog przedstawia implementację z XmlReader, która zawiera XML, który jest sprawdzany względem XSD, ale możesz to łatwo osiągnąć, bezpośrednio ładując XSD i parsując go.

2 klucz pomysł, aby znaleźć na blogu to:

// 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)); 

I rzeczywiście próbowała to samo w ubiegłym roku do analizowania XSD jako część skomplikowanej metody sprawdzania poprawności danych. Przez większą część tygodnia zrozumiałem, co się dzieje, i dostosowałem metody na blogu, by pasowały do ​​moich celów. Jest to zdecydowanie najlepszy sposób na wdrożenie dokładnie tego, co chcesz.

Jeśli chcesz wypróbować to w samodzielnym schemacie, możesz załadować XSD do obiektu XmlSchemaSet, a następnie użyć właściwości GlobalTypes, aby pomóc Ci znaleźć konkretny typ, którego szukasz.


EDIT: Wyciągnąłem mój stary kod i zaczął łącząc kod pomóc.

Najpierw załadować schematu:

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(); 
} 

następujące metody powinno dać blisko tego, co chcesz na podstawie XSD, który podałeś. Powinien on nadawać się do adaptacji do bardziej złożonych struktur.

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; 
} 

Następnie użycie jest bardzo proste, wystarczy wywołać metodę GetElementMaxLength(String) i powróci do Słownika nazwisk w formacie dostarczonych z wartości jako długość maksymalna:

var results = GetElementMaxLength("Setup_Type"); 

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

Twoje rozwiązanie działa doskonale i idealnie odpowiada moim wymaganiom. Jestem również w stanie uzyskać inne informacje, takie jak typ danych, wzorzec z elementu typu prostego w następujący sposób. – Jyina

+0

@Jyina można znaleźć wszelkiego rodzaju dobre informacje z XSD po wykryciu odpowiednich właściwości, aby spojrzeć na i prawej rzutować, aby. Odlewanie i sprawdzanie typu jest najtrudniejszą rzeczą do wymyślenia. – psubsee2003

+0

Dziękuję bardzo za pomoc i za poświęcenie czasu, aby pokazać mi kod. – Jyina

0

Proponowane rozwiązanie nie może być dokładnie czego szukasz. Prawdopodobnie wolisz używać klas System.Xml do obsługi takich informacji. Nie wiem, jak bardzo ogólny jest ten parser, w każdym razie to tylko moje 2 centy. Mój kod po prostu używa wyrażeń regularnych, zaprojektowanych tak, by poprawnie odpowiadały 99% możliwości (jak sądzę). Ktoś nazwałby to strzelaniem do muchy z pistoletu. W każdym razie to jest to:

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

Nie sądzę, Regex nadaje się do przetwarzania XML (XSD w tym przypadku). Na przykład, powiedzmy, że zdecyduję się nazwać moją przestrzeń nazw XSD 'xs' zamiast' xsd' ... – polkduran

+1

-1 dla użycia wyrażeń regularnych do parsowania XML, gdy istniejące biblioteki wbudowane w .NET już zrobią to samo i lepiej. – 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

Przypuszczam, że import byłby również przydatny: przy użyciu systemu; przy użyciu System.Collections.Generic; przy użyciu System.Linq; przy użyciu System.Windows.Forms; przy użyciu System.IO; using System.Xml; using System.Xml.Schema; przy użyciu System.Collections; –

Powiązane problemy