Walking XmlSchema and XmlSchemaSet objects

I’ve seen a number of newsgroup posts asking how to find a particular element or how to get a list of all elements from either XmlSchema or XmlSchemaSet objects.  Since we don’t provide this functionality in the framework, you need to manually traverse these objects to get what you want.  Depending on what your goal is, you might need to get either pre-compile or post-compile information.  For example, named groups are not available post-compile while PSVI information is not available pre-compile.  In this post I’ll show you how you can get pre-compile information from these objects.  To make the reading easier, I don’t include any error or exception handling code which is not relevant.

 

I’m assuming that you have a SchemaSet with a few schemas added to it.  SchemaSet provides collections of global elements, types, and attributes.  However, these collections are empty until you compile the set.  Note that named group collection is not available in the SchemaSet.  To get the pre-compile info (including a list of named groups), you will need to go through each schema in the set.

 

foreach (XmlSchema schema in ss.Schemas())

{

}

 

Once you have an XmlSchema object, you can step through and parse each global

 

// stepping through global complex types

foreach (XmlSchemaType type in schema.SchemaTypes.Values)

{

if (type is XmlSchemaComplexType)

      {

}

}

 

// stepping through global elements

foreach (XmlSchemaElement el in schema.Elements.Values)

{

}

 

// stepping through named groups

foreach (XmlSchemaAnnotated xsa in schema.Items)

{

if (xsa is XmlSchemaGroup)

{

}

}

 

Now that we have a global, whether it’s a type, an element, or a group, how do we traverse it?  I’m going to use a recursive method that takes an XmlSchemaParticle to do it.

 

void walkTheParticle(XmlSchemaParticle particle)

{

    if (particle is XmlSchemaElement)

    {

        XmlSchemaElement elem = particle as XmlSchemaElement;

        // todo: insert your processing code here

        if (elem.RefName.IsEmpty)

        {

            XmlSchemaType type = (XmlSchemaType)elem.ElementSchemaType;

            if (type is XmlSchemaComplexType)

            {

                XmlSchemaComplexType ct = type as XmlSchemaComplexType;

                if (ct.QualifiedName.IsEmpty)

                {

                    walkTheParticle(ct.ContentTypeParticle);

                }

            }

        }

    }

    else if (particle is XmlSchemaGroupBase)

    //xs:all, xs:choice, xs:sequence

    {

        XmlSchemaGroupBase baseParticle = particle as XmlSchemaGroupBase;

       foreach (XmlSchemaParticle subParticle in baseParticle.Items)

        {

            walkTheParticle(subParticle);

        }

    }

}

 

If the particle passed to the walkTheParticle is a base group (all, choice, or sequence), we will loop through each element within this base group.  If the particle is an element, we will do our processing and then (if the element is of a complex type) walk through it.  Finally, below are the last touches to the calling method to make it all work.

 

void start(XmlSchemaSet ss)

{

    foreach (XmlSchema schema in ss.Schemas())

    {

        foreach (XmlSchemaType type in schema.SchemaTypes.Values)

        {

            if (type is XmlSchemaComplexType)

            {

                XmlSchemaComplexType ct = type as XmlSchemaComplexType;

                walkTheParticle(ct.ContentTypeParticle);

            }

        }

        foreach (XmlSchemaElement el in schema.Elements.Values)

        {

            walkTheParticle(el);

        }

        foreach (XmlSchemaAnnotated xsa in schema.Items)

        {

            if (xsa is XmlSchemaGroup)

            {

                XmlSchemaGroup xsg = xsa as XmlSchemaGroup;

                walkTheParticle(xsg.Particle);

            }

        }

    }

}

 

That’s about it.  Let me know if you have any questions.