views:

428

answers:

4

I would like to create an XSD that defines an attribute which can be placed on elements from other schemas, or elements that are not in any schema. For example, the schema would look something like this:

<xs:schema id="MySchema"
    targetNamespace="http://tempuri.org/MySchema"
    elementFormDefault="qualified"
    xmlns="http://tempuri.org/MySchema"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
  <xs:attribute name="myAttribute" />
</xs:schema>

And the document might look something like this:

<someElement xmlns="http://tempuri.org/OtherSchema" xmlns:m="http://tempuri.org/MySchema"&gt;
  <someOtherElement someAttribute="value" m:myAttribute="value2" />
</someElement>

"OtherSchema" for this example looks like this:

<xs:schema id="OtherSchema"
    targetNamespace="http://tempuri.org/OtherSchema"
    elementFormDefault="qualified"
    xmlns="http://tempuri.org/OtherSchema"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
  <xs:element name="someElement">
    <xs:complexType>
      <xs:sequence>
        <xs:element minOccurs="0" maxOccurs="unbounded" name="someOtherElement">
          <xs:complexType>
            <xs:attribute name="someAttribute" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

A complete example, including a C# console application which performs validation, can be downloaded from http://dl.getdropbox.com/u/407740/SchemaTest.zip. My goal is to make this validate without having to modify "OtherSchema". Is this possible?

Thanks for your time!

A: 

Consider xsi:nil, xsi:schemaLocation and xsi:noNamespaceSchemaLocation. The answer is yes.

It also wouldn't have taken long to try it out and see.


You were missing a targetNamespace in the schema. Try this:

<xs:schema xmlns="MySchema" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="MySchema">
  <xs:attribute name="myAttribute" />
</xs:schema>
John Saunders
All your examples are from XML Schema instance namespace, attributes used by XML Schema itself. Could you give an example of OtherSchema schema that will allow MySchema attributes without declaring them in OtherSchema?
jelovirt
It doesn't matter. Try it and find out.
John Saunders
The example was actually a schema and a separate document. I have updated the question to reflect that. The point is, I have no control over the contents of OtherSchema. I want this attribute to be able to be placed on any element regardless of how it is defined in the schema it comes from.Obviously the example does not work or I would not have had a question. What are you suggesting I try?
jon without an h
What doesn't work? As it is now, if you reference your schema when the document is being validated, it should validate. Is there an exception? Can you show the code being used to validate it?
John Saunders
Currently I am using the XML editor in Visual Studio 2008. I have confirmed that VS is aware of my sample schema but when it validates the sample document it gives the error "The 'MySchema:myAttribute' attribute is not declared."
jon without an h
Actually, I tried it and it validated. However, I think that it should not have validated. In retrospect, unless a schema permits arbitrary attributes to be used, they are not valid. The answser from 13ren below appears to be the correct one, but as he said, it won't work for your example, since it uses elements and not complexType.
John Saunders
A: 

I had to add a wrapper, to import the two different schema into one (because xmllint only accepts a single xml schema):

<xs:schema id="Wrapper" xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
  <xs:import schemaLocation="MySchema.xsd" namespace="http://tempuri.org/MySchema"/&gt;
  <xs:import schemaLocation="OtherSchema.xsd" namespace="http://tempuri.org/OtherSchema"/&gt;
</xs:schema>

The only way I could get something like the Question to work was to edit OtherSchema,xsd (which is not allowed by the question), so append an attribute wildcard (after the existing one):

 <xs:attribute name="someAttribute" />
 <xs:anyAttribute namespace="##other"/>

I'm not enough of an expert of XML Schema to say "this is impossible", but it seems impossible to me.

One problem with your proposal is that you don't specify where the new attribute should appear. Usually, if you declare an attribute (or a complexElement, modelgroup etc), you are free to refer to it or not. If you don't explicitly refer to it, it has no effect. Therefore, I think your proposal will be treated as an attribute that is declared, but not referred to.

What you really want is a way to say "add this attribute to every existing complexType" - but you don't say this. And, unfortunately, there doesn't seem to be a way to say this. (there isn't even a way to say "add this attribute to this specific existing complexType" - you have to include it in the original definition or not at all.)

One way to partly do it is to <redefine> types in another schema - I'll add this in a second answer.

13ren
A: 

You can redefine schema, extending them however you like. In this way, you can modify the definitions of an existing schema without actually changing the file. BUT IT WON'T WORK FOR YOUR EXAMPLE as given, because one can't redefine elements (only complexTypes, etc. see http://www.w3.org/TR/xmlschema-1/#element-redefine). Therefore, I've broken your example into explicit complexTypes, so they are exposed for redefinition.

RedefineOtherSchema.xsd:

<xs:schema id="RedefineOtherSchema"
    targetNamespace="http://tempuri.org/OtherSchema"
    elementFormDefault="qualified"
    xmlns="http://tempuri.org/OtherSchema"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:m="http://tempuri.org/MySchema"&gt;          <!-- for the ref -->

  <xs:import schemaLocation="MySchema.xsd"
    namespace="http://tempuri.org/MySchema"/&gt;       <!-- import -->

  <xs:redefine schemaLocation="OtherSchema.xsd">    <!-- redefine -->
    <xs:complexType name="SomeOtherElement">
      <xs:complexContent>
        <xs:extension base="SomeOtherElement">
          <xs:attribute ref="m:myAttribute" />      <!-- the ref -->
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:redefine>
</xs:schema>

OtherSchema:

<xs:schema id="OtherSchema"
    targetNamespace="http://tempuri.org/OtherSchema"
    elementFormDefault="qualified"
    xmlns="http://tempuri.org/OtherSchema"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
  <xs:element name="someElement" type="SomeElement"/>

    <xs:complexType name="SomeElement">
      <xs:sequence>
        <xs:element minOccurs="0" maxOccurs="unbounded"
                    name="someOtherElement" type="SomeOtherElement"/>
      </xs:sequence>
    </xs:complexType>

    <xs:complexType name="SomeOtherElement">
      <xs:attribute name="someAttribute" />
    </xs:complexType>
</xs:schema>

MySchema: (unchanged)

<xs:schema id="MySchema"
    targetNamespace="http://tempuri.org/MySchema"
    elementFormDefault="qualified"
    xmlns="http://tempuri.org/MySchema"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
  <xs:attribute name="myAttribute"/>
</xs:schema>

Why <complexContent>? The redefinition must be an extension (or restriction) of the existing type - that's how it modifies the previous definition. Extension must be in <complexContent> (I believe).

Why <import>? You can't define things in more than one namespace in a xsd (there is only one "targetNamespace"). But you can get around this by importing a definition from another xsd (then you've not "defining" it). [is there is another way?]

HTH :-)

13ren
This is an interesting approach and it's possible I'll be able to adapt it to what I'm trying to do. It still places constraints on the third-party schema as you cannot redefine an inline type (and the particular schema I'm working with has several). But, it looks like you are right in saying that my original stated goal is not achievable, and this is a pretty good workaround.
jon without an h
I agree: it's common to define elements instead of types, and elements can't be defined. BTW: I think that types that are inline within a top-level type definition (not a top-level element) can be redefined, by redefining the type it is inside of.... (I haven't tried it, but it makes sense), but this doesn't help for top-level elements as far as I can see.
13ren
It would be worth asking on the xml-dev mailing list (there are some deep experts there, including some of the originators of XML Schema). The feature you want would be very useful - maybe they have a workaround.
13ren
A: 

This is exactly what NVDL (Namespace-based Validation and Dispatching Language) provides. It allows to combine multiple schemas/vocabularies to validate a document without the need to change those schemas. NVDL is an ISO standard.

A working NVDL script that handles your case is below

<rules xmlns="http://purl.oclc.org/dsdl/nvdl/ns/structure/1.0" startMode="other">
  <mode name="other">
    <namespace ns="http://tempuri.org/OtherSchema"&gt;
      <validate schema="other.xsd" useMode="validateMyAttributes"/>
    </namespace>
  </mode>
  <mode name="validateMyAttributes">
    <namespace ns="http://tempuri.org/MySchema" match="attributes">
      <validate schema="my.xsd"/>
    </namespace>
  </mode>
</rules>

Basically it says validate what it is on the ...tempuri.org/OtherSchema namespace with the other.xsd schema and the attributes from ...tempuri.org/MySchema with the my.xsd schema.

For more info about NVDL see www.nvdl.org. The above script was tested with oNVDL.

George Bina