2012-07-19 9 views
8

Tengo un XSD con múltiples tipos complejos y tipos simples (parte del archivo que se muestra a continuación). Necesito analizar este documento para obtener maxLength de cada uno de los tipos simples a los que se hace referencia en los tipos complejos. ¿Alguien puede por favor dar algunos consejos sobre cómo implementar esto? Necesito implementar esto de forma genérica, así que si realizo una consulta en "Setup_Type" debería dar el siguiente resultado. ¡Gracias!Cómo analizar un XSD para obtener la información de los elementos <xsd: simpleType> utilizando C#?

NewSetup/Cantidad = 12 (El nombre atributos de etiquetas de elementos separadas por "/" y maxLength de la simpleType anidada)

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

Gracias a todos por las excelentes soluciones. Fue muy útil mirar el código. Hasta ahora, he probado la solución de @ psubsee2003 y está funcionando muy bien para mí. – Jyina

Respuesta

17

He visto preguntas similares hechas en el pasado (la revelación completa, he pedir a un question similares a mí mismo). Analizar un XSD no es para los débiles de corazón.

Básicamente tiene 2 opciones, primero es más fácil de implementar, pero se puede romper más fácilmente mediante cambios menores en el XSD. el segundo es más robusto pero difícil de implementar.

Opción 1:

Analizar el XSD con LINQ (u otro C# analizador XML si lo prefiere). Como un XSD es solo un XML, puede cargarlo en un XDocument y simplemente leerlo a través de LINQ.

Por tan sólo una muestra de su propia 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> 

Puede acceder a la 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(); 

Esto no ofrece un método muy fácil para el análisis por el nombre del tipo, especialmente para extenderse tipos. Para usar esto necesitas saber la ruta exacta para cada elemento que estás buscando.

Opción 2:

Esto es demasiado complejo para una respuesta rápida (Nota: véase la edición abajo - Tenía algo de tiempo y poner juntos una solución de trabajo), así que voy a alentar a mirar mi propia pregunta que he vinculado anteriormente. En él, conecté un great blog que muestra cómo dividir seriamente el XSD en pedazos y podría permitirle realizar el tipo de búsqueda que desea. Tienes que decidir si vale la pena el esfuerzo para desarrollarlo (el blog muestra una implementación con XmlReader que contiene un XML validado contra el XSD en cuestión, pero puedes lograrlo cargando directamente el XSD y analizándolo).

2 idea clave para encontrar en el blog son:

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

He intentado lo mismo el año pasado para analizar un XSD como parte de un método de validación de datos complicada. Me llevó la mayor parte de una semana comprender realmente lo que sucedía y adaptar los métodos del blog para adaptarme a mis propósitos. Definitivamente es la mejor manera de implementar exactamente lo que desea.

Si desea probar esto con un esquema independiente, puede cargar el XSD en un objeto XmlSchemaSet, luego use la propiedad GlobalTypes para encontrar el tipo específico que está buscando.


EDIT: llegué a mi viejo código y comenzó la elaboración de un código para ayudarte.

En primer lugar para cargar su esquema:

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

Los siguientes métodos deben dar cierra a lo que quiere basado en el XSD que ya ha proporcionado. Debería ser bastante adaptable para tratar con estructuras más complejas.

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

A continuación, el uso es bastante simple, sólo tiene que llamar al método GetElementMaxLength(String) y ha de devolver un diccionario de los nombres en el formato que se proporciona con el valor que la longitud máxima:

var results = GetElementMaxLength("Setup_Type"); 

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

Su solución está funcionando bien para mí y se adapta perfectamente a mis requisitos. También puedo obtener la otra información, como el tipo de datos, el patrón del elemento del tipo simple de la siguiente manera. – Jyina

+0

@Jyina puedes encontrar todo tipo de buena información del XSD una vez que descubras las propiedades correctas para mirar y el molde correcto para hacer. El casting y la verificación de tipo es la parte más difícil de entender. – psubsee2003

+0

Muchas gracias por toda su ayuda y por tomarse el tiempo para mostrarme el código. – Jyina

0

Mi solución puede no ser exactamente Qué estás buscando. Probablemente prefiera usar clases System.Xml para manejar tales informaciones. No sé cuánto genérico le gustaría que sea este analizador, de todos modos estos son solo mis 2 centavos. Mi código solo usa expresiones regulares diseñadas para enfrentar el 99% de las posibilidades (supongo). Alguien llamaría esto disparar una mosca con una pistola. De todos modos eso es todo:

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

No creo que Regex sea adecuado para analizar XML (XSD en este caso). Por ejemplo, supongamos que decido nombrar mi espacio de nombres XSD 'xs' en lugar de' xsd' ... – polkduran

+1

-1 para usar expresiones regulares para analizar XML cuando las bibliotecas existentes incorporadas en .NET ya hacen lo mismo y mejor. – 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

Supongo que las importaciones también serían útiles: usando System; usando System.Collections.Generic; usando System.Linq; usando System.Windows.Forms; usando System.IO; usando System.Xml; usando System.Xml.Schema; usando System.Collections; –