4

I am trying to create an XML schema in which a lot of types are sharing some 'magic numbers'.

Instead of having to change my schema in several locations if/when these magic numbers change, I would like to pull them out into some kind of constant definition.

I have played around with adding a DTD to my schema and declaring some entities here. But I am by no means an expert on DTD, and while it seems to work in a C# application that uses the schema, there is also a native Win32 application that uses the same schema with msxml 4.0 where this blows up...

Does anyone have experience with extending the schema definition this way (can it be done), or is there a better way?

(EDIT: An example)

Example XML:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE schema [
   <!ENTITY SomeMagicNumber "10">]>
<xs:schema targetNamespace="http://tempuri.org/XMLSchema.xsd"
           elementFormDefault="qualified"
           xmlns:mstns="http://tempuri.org/XMLSchema.xsd"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:simpleType name="MySimpleType">
    <xs:restriction base="xs:int">
      <xs:maxInclusive value="&SomeMagicNumber;" />
    </xs:restriction>
  </xs:simpleType>

  <xs:complexType name="MyIntegers">
    <xs:sequence>
      <xs:element name="value" type="xs:int" maxOccurs="&SomeMagicNumber;" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="MyFloats">
    <xs:sequence>
      <xs:element name="value" type="xs:float" maxOccurs="&SomeMagicNumber;" />
    </xs:sequence>
  </xs:complexType>

</xs:schema>

Example Delphi Win32 code to load the schema:

var
  XmlSchemas: IXMLDOMSchemaCollection;
  XmlSchema: IXMLDOMDocument2;
  XmlDocument: IXMLDOMDocument2;
begin
  XmlSchemas := CoXMLSchemaCache40.Create;

  XmlSchema := CoDOMDocument40.Create;
  XmlSchema.load((*INSERT SCHEMA PATH HERE*));
  Assert(XmlSchema.parseError.errorCode = 0, XmlSchema.parseError.reason);
  XmlSchemas.add((*INSERT SCHEMA TARGET NAMESPACE HERE*), XmlSchema);

  XmlDocument := CoDOMDocument40.Create;
  XmlDocument.schemas := XmlSchemas;
  XmlDocument.validateOnParse := True;
end;

The code asserts after attempting to load the schema. Reason: 'The name of the top most element must match the name of the DOCTYPE declaration.'

RRUZ
  • 134,889
  • 20
  • 356
  • 483
Mathias Falkenberg
  • 1,110
  • 1
  • 11
  • 25

3 Answers3

2

A W3C Schema XSD is an XML document, so entities are allowed and supported. When the Schema file is read and processed, the entities will be expanded to produce the XML infoset.

http://www.xml.com/pub/a/2002/02/27/q-and-a.html

By the way, an XSD is itself an XML document, of course, so there's nothing preventing you from using entities within the Schema itself. (This is a little perverse, requiring the Schema to use a DTD to declare those entities.) You just can't use XML Schema to declare entities for use in other documents.

Entities can be a convenient way to avoid copy/paste and ease maintenance of XML instance files.

If it "blows up" in the native Win32 app when it parses the schema it sounds like a bug in MSXML 4.0 or the native Win32 app.

Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • I have posted a short example of something resembling what I am trying to do. – Mathias Falkenberg Jun 29 '11 at 07:16
  • The schema above will load in C# (provided I allow DTD in the XmlReader). The shown Delphi example will fail, because it cannot seem to match the DTD to the root element... I am by no means an expert on DTD - Does the way I am declaring the DTD here make it an internal definition, which has nothing to do with the _real_ DTD of XMLSchema? Could I instead _extend_ the XMLSchema.dtd? – Mathias Falkenberg Jun 29 '11 at 07:30
2

Yes, you can use entities to define constants in an XML Schema file.

The code asserts after attempting to load the schema. Reason: 'The name of the top most element must match the name of the DOCTYPE declaration.'

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE schema [
   <!ENTITY SomeMagicNumber "10">
]>
<xs:schema targetNamespace="http://tempuri.org/XMLSchema.xsd"
           elementFormDefault="qualified"
           xmlns:mstns="http://tempuri.org/XMLSchema.xsd"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">

    ...(clipped away)

</xs:schema>

Your problem is caused by the fact that DTDs are not namespace aware. Because of this, the parser sees a DTD that defines the root element <schema> whereas your document has a root element <xs:schema>. Try using <!DOCTYPE xs:schema [ instead. This prefix "hardcoding" might seem erroneous but in DTD there is no easy general way to have a namespace prefix mapping.

If you use the same "magic numbers" in several schemas, then you could also define the entities in a separate DTD and then include that by referring to it via an parameter entity in your embedded DTD.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xs:schema [
  <!ENTITY % magicNumbers SYSTEM "url/to/your/entity/dtd-document">
  %magicNumbers;
]>
<xs:schema ... >
jasso
  • 13,736
  • 2
  • 36
  • 50
  • This seems to work. Unfortunately, this also seems to fudge up intellisense in the Visual Studio XML editor... Win some, lose some, I guess.. – Mathias Falkenberg Jul 11 '11 at 06:41
0

You can make a common xsd, and import it from the other schemas. See Importing Types.

The report schema, report.xsd, makes use of the simple type xipo:SKU that is defined in another schema, and in another target namespace.

<import namespace="http://www.example.com/IPO"/>
Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
  • While importing common schemas is useful, it doesn't seem to be of much help in the current matter. I would like to declare entities (constants) in my schema, not share types. The post by Mads Hansen seems to imply that this is possible - but that I am doing it wrong, somehow... – Mathias Falkenberg Jun 29 '11 at 08:27