Reputation: 853
I am attempting to update attributes in typed XML. I have done this in the past by building the XML in untyped objects then setting it to a typed object (so avoiding the issue), but I'd like to know how to modify the typed data directly.
My schema is:
if exists (select [xml_collection_id]
from sys.[xml_schema_collections] as [xsc]
where [xsc].name = 'xsc_test_stack'
and [xsc].[schema_id] = schema_id(N'chamomile'))
drop xml schema collection [chamomile].[xsc_test_stack];
go
create xml schema collection [chamomile].[xsc_test_stack] as N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:chamomile="https://github.com/KELightsey/chamomile" targetNamespace="https://github.com/KELightsey/chamomile">
<xsd:element name="test_stack">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="description" type="xsd:string" minOccurs="1" maxOccurs="1" />
<xsd:element name="test_stack_detail" type="chamomile:any_complex_type" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="test" type="chamomile:any_complex_type" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="test_count" type="xsd:int" use="required" />
<xsd:attribute name="pass_count" type="xsd:int" use="required" />
<xsd:attribute name="timestamp" type="xsd:dateTime" use="required" />
<xsd:anyAttribute processContents="lax" />
</xsd:complexType>
</xsd:element>
<xsd:complexType name="any_complex_type">
<xsd:complexContent>
<xsd:restriction base="xsd:anyType">
<xsd:sequence>
<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
</xsd:sequence>
<xsd:anyAttribute processContents="lax" />
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>';
go
Samples of the type of structures I build are:
declare @test [xml]([chamomile].[xsc_test_stack]) = N'
<chamomile:test_stack xmlns:chamomile="https://github.com/KELightsey/chamomile" name="[chamomile].[person_test].[get_age]" test_count="2" pass_count="2" timestamp="2018-06-24T15:50:19.3466667">
<description>This test stack consists of tests which validate the functionality of the age calculation for a person.</description>
</chamomile:test_stack>';
go
declare @test [xml]([chamomile].[xsc_test_stack]) = N'
<chamomile:test_stack xmlns:chamomile="https://github.com/KELightsey/chamomile" name="[chamomile].[person_test].[get_age]" test_count="2" pass_count="2" timestamp="2018-06-24T15:50:19.3466667">
<description>This test stack consists of tests which validate the functionality of the age calculation for a person.</description>
<test_stack_detail>
<any_valid_xml_goes_here />
</test_stack_detail>
</chamomile:test_stack>';
go
What I have tried is:
set @test_stack.modify(N'replace value of (//@test_count)[1] with sql:variable("@count")');
This returns: Msg 9306, Level 16, State 1, Line 28 XQuery [modify()]: The target of 'replace value of' cannot be a union type, found '(attribute(test_count,xs:int) | attribute(test_count,xs:anySimpleType)) ?'.
set @test_stack.modify(N'declare namespace chamomile="https://github.com/KELightsey/chamomile";
replace value of (chamomile:test_stack/@test_count)[1] with sql:variable("@count")');
This returns: Msg 9306, Level 16, State 1, Line 25 XQuery [modify()]: The target of 'replace value of' cannot be a union type, found '(attribute(test_count,xs:int) | attribute(test_count,xs:anySimpleType)) ?'.
I've spent hours Google mining this. There are many, many examples with untyped XML, and a few with typed XML that still throw the same exceptions.
I'd appreciate some insight.
Thanks, Katherine
Upvotes: 0
Views: 106
Reputation: 67311
I must admit, that I did not find any straight-forward approach too. Seems to be a bug, at least I don't see any sense in this. A workaround is a simple cast, but this is very close to the work around you are describing in your initial lines:
declare @test [xml]([chamomile].[xsc_test_stack]) =
N'<chamomile:test_stack xmlns:chamomile="https://github.com/KELightsey/chamomile" name="[chamomile].[person_test].[get_age]" test_count="2" pass_count="2" timestamp="2018-06-24T15:50:19.3466667">
<description>This test stack consists of tests which validate the functionality of the age calculation for a person.</description>
</chamomile:test_stack>';
DECLARE @cnt INT=99;
DECLARE @intermediate XML=CAST(@test AS XML); --magic happens here
SET @intermediate.modify('declare namespace chamomile="https://github.com/KELightsey/chamomile";
replace value of (chamomile:test_stack/@test_count)[1] with sql:variable("@cnt")');
SET @test=@intermediate; --re-assign to typed XML
SELECT @test;
Upvotes: 1