THE SQL Server Blog Spot on the Web
Welcome to SQLblog.com - The SQL Server blog spot on the web Sign in | Join | Help
in Search

Xml Schema Conundrum

Last post 06-25-2008, 13:11 by Steve. 2 replies.
Sort Posts: Previous Next
  •  06-16-2008, 10:20 7315

    Xml Schema Conundrum

    I'm working with typed xml in Sql server '05 and haven't been able to resolve a couple questions through the documentation or any of my searches so far - so fanfare aside here is the most immediate question:

    If I have an XML Schema Collection with multiple namespaces how can I specify an xml column or variable that only targets one of the included namespaces?

    i.e: (pretend this is SSMS)
    - XML Schema Collections
    ----- dbo.FooSchema
    --------- ns://mydomain.com/foo/
    --------- ns://mydomain.com/importsFoo/

    Is it possible to have a typed xml column/variable that is valid only if the content conforms to the 'ns://mydomain.com/importsFoo/' namespace?


    Aside from this, if anyone has discovered any creative ways to resolve an xs:import against a schema stored in a different collection I'd love to hear about it.

    In essence I have several namespaces under the same ns root and I would like to use them in schema collections to validate and operate on xml in the database, but I'm finding advanced support lacking. I'll eventually become an expert on the topic through trial and error, but I was hoping someone had already beat me to it.

    So, any bytes?

    Thanks;

    Steve L.
  •  06-18-2008, 0:14 7359 in reply to 7315

    Re: Xml Schema Conundrum

    >> Aside from this, if anyone has discovered any creative ways to resolve an xs:import against a schema stored in a different collection I'd love to hear about it.

    Not likely to happen. Schemas are compiled into types at cataloging so they can be statically analyzed by the query optimizer. It would be nice to say that a column is typed by n-many schemas.

    >> Is it possible to have a typed xml column/variable that is valid only if the content conforms to the 'ns://mydomain.com/importsFoo/' namespace?

    I may be (and probably am) misunderstanding your question, but this seems to work. Remember, typed XML instances are fragments unless they specifically marked as documents. I think the answer is "yes" provided you are treating the instances as fragments.

    use scratch;
    go
    drop table dbo.typedInstances;
    drop xml schema collection dbo.exampleSchema;
    go
    create xml schema collection dbo.exampleSchema as '<xs:schema xmlns:inner="http://schemas.develop.com/inner/" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://schemas.develop.com/inner/">
      <xs:complexType name="T_someCollection">
        <xs:sequence>
          <xs:element ref="inner:collectionItem" maxOccurs="unbounded"/>
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="T_collectionItem">
        <xs:sequence>
          <xs:element ref="inner:value"/>
          <xs:element ref="inner:text"/>
        </xs:sequence>
      </xs:complexType>
      <xs:element name="value" type="xs:byte"/>
      <xs:element name="text" type="xs:string"/>
      <xs:element name="someCollection" type="inner:T_someCollection"/>
      <xs:element name="collectionItem" type="inner:T_collectionItem"/>
    </xs:schema>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://schemas.develop.com/outer/">
      <xs:element name="someString" type="xs:string"/>
      <xs:element name="someDate" type="xs:dateTime"/>
    </xs:schema>
    <xs:schema xmlns:inner="http://schemas.develop.com/inner/" xmlns:outer="http://schemas.develop.com/outer/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:import namespace="http://schemas.develop.com/outer/"/>
      <xs:import namespace="http://schemas.develop.com/inner/"/>
      <xs:complexType name="T_root">
        <xs:sequence>
          <xs:element ref="outer:someString"/>
          <xs:element ref="outer:someDate"/>
          <xs:element ref="inner:someCollection"/>
        </xs:sequence>
      </xs:complexType>
      <xs:element name="root" type="T_root"/>
    </xs:schema>';
    go
    create table dbo.typedInstances(
      id tinyint not null identity(1,1) primary key,
      content xml(dbo.exampleSchema) not null);
    go
    insert into dbo.typedInstances values (
    '<?xml version="1.0" encoding="UTF-8"?>
    <root xmlns:outer="http://schemas.develop.com/outer/" xmlns:inner="http://schemas.develop.com/inner/">
      <outer:someString>Example</outer:someString>
      <outer:someDate>2008-06-17T22:46:00-05:00</outer:someDate>
      <inner:someCollection>
        <inner:collectionItem>
          <inner:value>1</inner:value>
          <inner:text>lawyers</inner:text>
        </inner:collectionItem>
        <inner:collectionItem>
          <inner:value>2</inner:value>
          <inner:text>guns</inner:text>
        </inner:collectionItem>
        <inner:collectionItem>
          <inner:value>3</inner:value>
          <inner:text>moeny</inner:text>
        </inner:collectionItem>       
      </inner:someCollection>
    </root>');
    go
    insert into dbo.typedInstances values (
    '  <someString xmlns="http://schemas.develop.com/outer/">Example</someString>
      <someDate xmlns="http://schemas.develop.com/outer/">2008-06-17T22:46:00-05:00</someDate>');
    go

    insert into dbo.typedInstances values (
    '  <someCollection xmlns="http://schemas.develop.com/inner/">
        <collectionItem>
          <value>1</value>
          <text>lawyers</text>
        </collectionItem>
        <collectionItem>
          <value>2</value>
          <text>guns</text>
        </collectionItem>
        <collectionItem>
          <value>3</value>
          <text>moeny</text>
        </collectionItem>       
      </someCollection>');
    go
    select content from dbo.typedInstances;
    go
     

    Is that what you had in mind?




    Thanks,
    Kent Tegels
    DevelopMentor
  •  06-25-2008, 13:11 7504 in reply to 7359

    Re: Xml Schema Conundrum

    Thanks for the example and for attempting to answer my question, I've been posting it all over the place and have had minimal response, granted it's a small bit of business in Sql Server. My question might have been a bit vague, I'd hoped that by keeping it simple it might spark more conversation.

    So, to refine the issue using your example (thanks again for that); let's say that we've implemented the above example and have those structures in place. I am storing document fragments, what I want to do is have a column that will only store fragments conforming to the 'http://schemas.develop.com/outer/' namespace. Given the example above, with the type of restriction I'm seeking, only the 2nd / middle insert would succeed. The first and last would fail because they do not exclusively implement the  'http://schemas.develop.com/outer/' namespace.

    Does that help make my question more concise?

    Can that degree of control be achieved in any way using a typed xml declaration, does it require a constraint, or is there a best practices recommendation that covers this concept that I haven't found yet?

    At this point I'm fairly resigned to using a check constraint to provide the additional validation necessary, there certainly are worse options.

    While working with the example and testing a check constraint using a UDF I've noticed a limitation when using the exist() method, it only works on schemas that have a single root node. Last time I checked a schema could legally have multiple root nodes as the example above does in the inner and outer namespaces. However any usage of exist() with any of the schemas having multiple roots appears to fail, sometimes silently, in all contexts.

    When I tried to create a check constraint that would validate the outer schema by specifying .exist('/someString') the create function statement would fail with:
    XQuery [exist()]: There is no element named 'someString'

    Same thing for someDate or any other element from inner or outer, only /root works since its schema has only one root element.

    i did some more testing with the schema bound column and select statements and found the same is true outside of a function and remains true even if I remove the schema collection binding from the column (turn it into an untyped xml column). I'm guessing that's because the xml content still specifies the namespace and sql server is smart enough to associate the namespace in the schema colelction with the namespace declared in the element.

    When I insert some instances of <someString /> into the untyped column without the xmlns attribute .exist('/someString') miracuously returns the expected value (1).

    I don't recall seeing this behavior specifically documented anywhere, but I would certianly believe it if someone told me it's buried in the documentation somewhere.

    In my scenario I'm also writing the schema and have the option of providing namespaces with a single root element for validation, but I could see where this behavior could be frustrating if you had to discover it the hard way working with an immutable schema - you'd pretty much have to revert to string parsing or use the CLR to validate xml against a schema with multiple root elements.

    Thanks very much for addressing the issue, if you had not I'd probably have discovered the .exist() issue the had way.

    ~Steve

     

Powered by Community Server (Commercial Edition), by Telligent Systems
  Privacy Statement