Modify xml with namespaces Modify xml with namespaces xml xml

Modify xml with namespaces


It got a bit more ugly than I hoped. Mainly because I can't use sql:variable("@enumeration")/delete-me/node() inside a .modify().

I assume, that you can modify the generation of the @enumeration, as follows:

CREATE TABLE #list (value nvarchar(100));INSERT  INTO #listVALUES  ('item 1');INSERT  INTO #listVALUES  ('item 2');INSERT  INTO #listVALUES  ('item 3');INSERT  INTO #listVALUES  ('item 4');INSERT  INTO #listVALUES  ('item 5');INSERT  INTO #listVALUES  ('item 6');DECLARE @enumeration AS xml;WITH XMLNAMESPACES('http://www.w3.org/2001/XMLSchema' AS xs)SELECT @enumeration = (    SELECT value AS '@value'    FROM #list FOR XML PATH('xs:enumeration'), ROOT('delete-me'), TYPE);

The idea is to use FOR XML with ROOT, so that the generated namespace is at the unnecessary root element (which can be skipped). Otherwise we would have to recreate the xs:enumeration-elements later.

Two solutions

Use .modify() three times

The idea:

  1. We insert everything from @enumeration (with the unnecessary root) somewhere into the other xml
  2. We copy the desired content to the right place
  3. We delete the no longer needed copy of @enumeration in @schema.
DECLARE @schema xml;SET @schema = '<xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="test">    <xs:element name="test" msdata:IsDataSet="true" msdata:MainDataTable="Example" msdata:UseCurrentLocale="true">        <xs:complexType>            <xs:choice maxOccurs="unbounded">                <xs:element name="Example">                    <xs:complexType>                        <xs:sequence>                            <xs:element name="myList" minOccurs="1" nillable="false">                                <xs:simpleType>                                    <xs:restriction base="xs:string">                                        <xs:maxLength value="50" />                                    </xs:restriction>                                </xs:simpleType>                            </xs:element>                        </xs:sequence>                    </xs:complexType>                </xs:element>            </xs:choice>        </xs:complexType>    </xs:element></xs:schema>';SET @schema.modify('insert sql:variable("@enumeration")    into /');SET @schema.modify('declare namespace xs="http://www.w3.org/2001/XMLSchema";insert /delete-me/node()    into (//xs:element[@name=''myList'']/xs:simpleType/xs:restriction)[1]');SET @schema.modify('delete /delete-me');SELECT  @schema;DROP TABLE #list;

.query() to create @schema

If you can change the part where @schema is created, you can generate @schema directly as .query() from @enumeration:

DECLARE @enumeration AS xml;WITH XMLNAMESPACES('http://www.w3.org/2001/XMLSchema' AS xs)SELECT @enumeration = (    SELECT value AS '@value'    FROM #list FOR XML PATH('xs:enumeration'), ROOT('delete-me'), TYPE);DECLARE @schema xml;SET @schema = @enumeration.query('<xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="test">    <xs:element name="test" msdata:IsDataSet="true" msdata:MainDataTable="Example" msdata:UseCurrentLocale="true">        <xs:complexType>            <xs:choice maxOccurs="unbounded">                <xs:element name="Example">                    <xs:complexType>                        <xs:sequence>                            <xs:element name="myList" minOccurs="1" nillable="false">                                <xs:simpleType>                                    <xs:restriction base="xs:string">                                        <xs:maxLength value="50" />                                        {/delete-me/node()}                                    </xs:restriction>                                </xs:simpleType>                            </xs:element>                        </xs:sequence>                    </xs:complexType>                </xs:element>            </xs:choice>        </xs:complexType>    </xs:element></xs:schema>');SELECT  @schema;


You can do a simple string replace if the xml isn't too big:

DECLARE @schemaVARCHAR (MAX)SET @schemaVARCHAR = CAST(@schema AS VARCHAR(MAX))SET @schemaVARCHAR =     REPLACE(        @schemaVARCHAR,         '<xs:enumeration xmlns:xs="http://www.w3.org/2001/XMLSchema" value="item 1" />',        ''    )SET @schema = CAST(@schemaVARCHAR AS XML)


If your XML isn't too complicated, why not use FOR XML EXPLICIT instead?

Another workaround would be to get generate the XML without the namespace and then add it in afterwards.