Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XmlSchema inferred from an XML file - how to iterate through all the elements in the XSD?

Tags:

c#

xml

xsd

I have an XML file and I'm inferring its XSD schema in run-time, using the XmlSchemaInference class.

Sample file:

<products>
    <product id="1" name="t-shirt">
        <size name="medium"/>
        <size name="large"/>
        <price>
            <net>10</net>
            <gross>25</gross>
        </price>
    </product>
    <product id="2" name="computer mouse">  
        <price>
            <net>50</net>       
        </price>
    </product>
</products>

It does work - it infers the schema nicely:

<?xml version="1.0"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="products">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="product">
          <xs:complexType>
            <xs:sequence>
              <xs:element minOccurs="0" maxOccurs="unbounded" name="size">
                <xs:complexType>
                  <xs:attribute name="name" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
              <xs:element name="price">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="net" type="xs:unsignedByte" />
                    <xs:element minOccurs="0" name="gross" type="xs:unsignedByte" />
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="id" type="xs:unsignedByte" use="required" />
            <xs:attribute name="name" type="xs:string" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

The question is:

How can I iterate (recursively?) through all the elements from this schema? How are they stored by the XmlSchemaSet class? I need to present them to the user so they can do some mapping.

I am retrieving an XmlSchema from XmlSchemaSet.Schemas property, and then what? XmlSchema.Elements only contains one item (products), and I can't find any way to look up what its subelements are.

like image 741
Konrad Morawski Avatar asked Sep 05 '11 19:09

Konrad Morawski


1 Answers

Okay! No answer and not much interest - I figured it out on my own.

I used code from this MSDN article I googled up: Traversing XML Schemas

And I based my recursive solution on it.

void PrintSchema(string xmlFilePath)
{
    var schemaSet = new XmlSchemaInference().InferSchema(XmlReader.Create(xmlFilePath));
    foreach (XmlSchemaElement element in schemaSet
        .Schemas()
        .Cast<XmlSchema>()
        .SelectMany(s => s.Elements.Values.Cast<XmlSchemaElement>()))
    {
        Debug.WriteLine(element.Name + " (element)");
        IterateOverElement(element.Name, element);
    }
}

void IterateOverElement(string root, XmlSchemaElement element)
{
    var complexType = element.ElementSchemaType as XmlSchemaComplexType;
    if (complexType == null) 
    {
        return;
    }
    if (complexType.AttributeUses.Count > 0)
    {
        var enumerator = complexType.AttributeUses.GetEnumerator();
        while (enumerator.MoveNext())
        {
            var attribute = (XmlSchemaAttribute)enumerator.Value;
            Debug.WriteLine(root + "." + attribute.Name + " (attribute)");
        }
    }
    var sequence = complexType.ContentTypeParticle as XmlSchemaSequence;
    if (sequence == null) 
    {
        return;
    }
    foreach (XmlSchemaElement childElement in sequence.Items)
    {
        root += String.Concat(".", childElement.Name);
        Debug.WriteLine(root + " (element)");
        // recursion
        IterateOverElement(root, childElement);
    }
}

The output is:

products (element)
products.product (element)
products.product.id (attribute)
products.product.name (attribute)
products.product.size (element)
products.product.size.name (attribute)
products.product.price (element)
products.product.price.net (element)
products.product.price.gross (element)

I leave to you to judge how friendly this API is, especially given how scarce is the MSDN documentation on these particular classes. Any comments or insights are appreciated.

like image 167
Konrad Morawski Avatar answered Feb 05 '23 09:02

Konrad Morawski