Multiple Xsd schemas in 1 file

aly picture aly · Mar 25, 2015 · Viewed 17.6k times · Source

I have multiple Xsd schema files (small ones) and would like to somehow combine them into 1 big file, I was thinking of an xml file with 1 node for each Xsd schema content, in the C# code selecting the corresponding section and validating against it. The problem is I can see a warning "The global element 'xxx' has already been declared. " when opening the Xml file using Visual studio (I'm definitely not an Xsd expert; I indeed have the same element repeated).

Any ideas of the correctness of this approach ? Shall I use CData for Xsd content may be instead ?

Examples of Xml files:

Xml 1:

<resource xmlns="">
  <identifier>5401902302111</identifier>
  <product>printer</product>
  <requestedby />
</resource>

Xml 2:

<resource xmlns="">
  <identifier>5401902302112</identifier>
  <email>[email protected]</email>
</resource>

Xsd 1:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xs:element name="resource">
              <xs:complexType>
                <xs:sequence>
                  <xs:element type="xs:long" name="identifier"/>
                  <xs:element type="xs:string" name="requestedby"/>
                  <xs:element type="xs:string" name="product"/>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:schema>

Xsd 2:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xs:element name="resource">
              <xs:complexType>
                <xs:sequence>
                  <xs:element type="xs:long" name="identifier"/>
                  <xs:element type="xs:string" name="email"/>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:schema>

What I want to have in the end:

*<?xml version="1.0" encoding="utf-8"?>
<resource>
  <resourcedata type="acquisition">
    <details>
      <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:element name="resource">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:long" name="identifier"/>
              <xs:element type="xs:string" name="requestedby"/>
              <xs:element type="xs:string" name="product"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:schema>
    </details>
  </resourcedata>
  <resourcedata type="warningletter">
    <details>
      <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:element name="resource">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:long" name="identifier"/>
              <xs:element type="xs:string" name="email"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:schema>
    </details>
  </resourcedata>
</data>*

C# code:

var xsdContent =
                xDoc.Element("resource").Elements("resourcedata")
                    .Where(
                        x =>                            
                            x.Attribute("type").Value == "acquisition")
                    .FirstOrDefault().Element("details").FirstNode.ToString();            


            var doc = new XDocument(xElementContent);

            XmlSchemaSet schemas = new XmlSchemaSet();
            schemas.Add("", XmlReader.Create(new StringReader(xsdContent)));

            Console.WriteLine("Validating doc");
            bool errors = false;
            doc.Validate(schemas, (o, e) =>
            {
                Console.WriteLine("{0}", e.Message);
                Console.WriteLine("{0}", e.Exception);
                errors = true;
            });

            Console.WriteLine("doc {0}", errors ? "did not validate" : "validated");

(xElementContent variable contains Xml content)

Thanks

Answer

Petru Gardea picture Petru Gardea · Apr 19, 2015

Unlike others, I would think that the only problem with your question is that it fails to illustrate a good example as to why would one create an XML file format as described by you. In fact, I was involved in an interesting discussion here on SO, for which C. M. Sperberg-McQueen even put together a demo, concluding:

It thus illustrates that multiple spec-conformant schema documents can be embedded in the same XML document. (Since this appears to be an issue for some interested parties, it should apparently also be noted that WSDL is not involved here.)

I felt compelled to link C. M. Sperberg-McQueen's answer, since you said I was thinking of an xml file rather than an XSD file (which would be kjhughes point).

Why I think you didn't show a good scenario, is because the following single schema would satisfy very well your XMLs.

<?xml version="1.0" encoding="utf-8"?>
<!--XML Schema generated by QTAssistant/XML Schema Refactoring (XSR) Module (http://www.paschidev.com)-->
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="resource">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="identifier" type="xs:unsignedLong"/>
                <xs:choice>
                    <xs:element minOccurs="0" name="email" type="xs:string"/>
                    <xs:sequence>
                        <xs:element minOccurs="0" name="product" type="xs:string"/>
                        <xs:element minOccurs="0" name="requestedby" type="xs:string"/>                             
                    </xs:sequence>
                </xs:choice>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

Let's go through some scenarios (please update your question if you have something else):

  • Your root element might have a different name. In this case, simply create another global element, matching that name.
  • Your root element might be in a different namespace. In this case, simply create another XSD file, with a targetNamespace matching your XML's namespace. Then add all your XSDs to an XmlSchemaSet, and validate away.
  • For the same element, your content models may be different, yet reconcilable in terms of what an XSD 1.0 (since you're showing stock .NET code, I assume you're stuck with what .NET supports). This would be the case with the schema I've created above.
  • You can't use XSD 1.0; it may, or not, work with features in XSD 1.1 (such as co-constraints, type alternatives, etc.) - point is, XSD 1.1 is not employable here. Then your proposal to put together multiple XSDs in one XML file will work like a charm. In fact, I would call it a poor man's alternative for XSD 1.1 in the .NET world...

For the latter, I would recommend a slightly different approach. I would use XInclude to link the XSDs toghether in one file - if that's what your processing model requires; there's a NET library here. This would allow to author the XSDs independently, thus making sure each one is valid, and using common editors (including your Visual Studio).