I am trying to modify the attribute of an XML root attribute in XQuery with T-SQL but I don't manage to do that. My XML has a namespace in it and I can't exactly bypass this. When I query the value of the xml I successfully retrieve the value because I use: ;WITH XMLNAMESPACES (DEFAULT 'some:namespace:here:v1'). I also tried to use: 'declare default element namespace "some:namespace:here:v1";' in the XQuery but does not seem to work.
Any ideas of how can I achieve this ?
This is an example of the XML I am trying to modify.
DECLARE #XML_TO_READ XML = N'
<F2101 xmlns="some:namespace:here:v1" propertyToModify="valueToModify">
<person xmlns="some:namespace:here:v1" anotherPropertyToModify="anotherValueToModify" />
</F2101>'
I retrieve the value like this:
;WITH XMLNAMESPACES (DEFAULT 'some:namespace:here:v1')
SELECT propertyToModify =
#XML_TO_READ.value('(/F2101/#propertyToModify)[1]', 'nvarchar(50)')
And I tried to modify (update) the value like this:
SET #XML_TO_READ.modify('
declare default element namespace "some:namespace:here:v1";
replace value of (/F2101/propertyToModify/text())[1] with ("modifiedValue")')
I tried multiple solutions but I did not find anything that would work for my special case here.
Thanks in advance.
Your statement should be:
SET #XML_TO_READ.modify('
declare default element namespace "some:namespace:here:v1";
replace value of (/F2101/#propertyToModify)[1] with ("modifiedValue")')
Note the "#propertyToModify" rather than "propertyToModify/text()".
Also, documentation link: replace value of (XML DML).
Related
#x xml:'
<a number="1">
<b>1</b>
</a>'
I want use query() to get value (can't use value())
#x.query('string(/a[1]/b[1])')
is ok.
#x.query('string(/a[#number="1"]/b)')
throws an error.
Do you have any solution? I want use [#number="1"] to get value.
You can try this.
#x.query('string( (/a[#number="1"]/b)[1])')
In most cases one wants to read more than one value from a given node. You can use a combination of .nodes() (very related to .query(), but returning a derived table) and .query() or .value() like here:
DECLARE #x xml=
N'<a number="1">
<b>1</b>
</a>';
SELECT MyA.value(N'(b/text())[1]','int') AS ReadTheValue
,MyA.query(N'b') QueryTheValue
FROM #x.nodes(N'/a[#number="1"]') AS A(MyA);
You can pass in the search value as a variable like here:
DECLARE #SearchNumber INT=1;
SELECT MyA.value(N'(b/text())[1]','int') AS ReadTheValue
,MyA.query(N'b') QueryTheValue
FROM #x.nodes(N'/a[#number=sql:variable("#SearchNumber")]') AS A(MyA);
If you need nothing else then the value through .query() go with the solution provided by Serkan Aslan. You were just missing to ensure the inner experession to be singleton.
But I must admit, that I have no idea, why one should need this...
XML Schema: (Assumes that the XMLNAMESPACES are already set AS a and b)
<Layer1 xmlns="a">
<Layer2 xmlns="b">
<Layer3>
<id>val1</id>
<data>False</data>
</Layer3>
<Layer3>
<id>val2</id>
<data>True</data>
</Layer3>
</Layer2>
</Layer1>
I am using this bit of sql to attempt to accomplinsh my task.
ITEM.value('(/a:Layer1/b:Layer2/b:Layer3)[1]', 'varchar(max)') AS ReturnValue
What I am trying to do is get only the true values where id='val2' AND data='True'. Something like this:
ITEM.value('(/a:Layer1/b:Layer2/b:Layer3[id="val2" and data="True"]/b:data)[0]', 'varchar(max)') AS ReturnValue
For some reason the above returns null. I'd assume it is a syntax error.
In this query I would like the value to be returned as True from any Layer3 Parent where the id and data are following the conditions. I'd appreciate any help and thank you in advance.
From your code I take, that this is SQL-Server...
Your XML defines a default namespace for the first node <Layer1> and again a default namespace for the <Layer2>.
That means, that all elements below <Layer2> are in this new default namespace.
Your code misses the namespaces here [id="val2" and data="True"], but I do not really understand what you are trying to achieve actually...
DECLARE #ITEM XML=
N'<Layer1 xmlns="a">
<Layer2 xmlns="b">
<Layer3>
<id>val1</id>
<data>False</data>
</Layer3>
<Layer3>
<id>val2</id>
<data>True</data>
</Layer3>
</Layer2>
</Layer1>';
--This query will find the value "True" within <data> below a <Layer3>, where the <id> is "val2":
WITH XMLNAMESPACES('a' AS a,'b' AS b)
SELECT #ITEM.value('(/a:Layer1/b:Layer2/b:Layer3[b:id="val2"]/b:data/text())[1]', 'varchar(max)');
You might use a *: in front of each and any name to use a namespace wildcard:
SELECT #ITEM.value('(/*:Layer1/*:Layer2/*:Layer3[*:id="val2"]/*:data/text())[1]', 'varchar(max)');
One more approach was to define your inner default ns like this, which saves some typing:
WITH XMLNAMESPACES('a' AS a, DEFAULT 'b')
SELECT #ITEM.value('(/a:Layer1/Layer2/Layer3[id="val2"]/data/text())[1]', 'varchar(max)');
I have this XML:
i want to get the value on Property name = "ParticipTypeName" i am using something like that:
;WITH XMLNAMESPACES(DEFAULT 'http://xml.common.asset.aoma.sonymusic.com/ProductMetadata.xsd')
SELECT
x.u.value('(/BusinessUnitProperties/Property[#name = "ParticipTypeName"])[1]', 'varchar(100)') as ParticipTypeName
from
#XML.nodes('/ProductMetadata/Tracks/Track/Participants/Participant') x(u)
it doesn't work.
How I should get the value in this property?
Try this:
SELECT x.u.value('(//*:Property[#*:name="ParticipTypeName"])[1]','nvarchar(max)')
The // will search for any element <Property>. The XQuery-filter will choose the one with the name you are looking for. The *: will allow you to ignore the namespace.
I've constructed some XML in TSQL.
declare #requestXML xml
set #requestXML = (
select #dataXML
for xml raw ('rtEvent')
The general output for what I now have follows the pattern resembling this:
<rtEvent>
<ctx>
.....
</ctx>
</rtEvent>
What I'd like to do now is add some attributes and values to the rtEvent root
element node but I'm not certain how to achieve it.
I've looked at the Modify method of the XML object and have observed the insert, replace value of, and delete operations but cannot seem to figure out how to use any of them to achieve the results I'm after.
Basically, I want to be able to modify the root node to reflect something like:
<rtEvent type="customType" email="someaddress#domain.com"
origin="eCommerce" wishedChannel="0" externalId="5515">
<ctx>
...
</ctx>
</rtEvent>
Should I be using the documented XML.Modify or is there a better method? How should it be done?
Just in case you wanted to see the modify method way of doing it:
DECLARE #requestXML XML = '<rtEvent><ctx>...</ctx></rtEvent>'
SET #requestXML.modify(
'insert
(
attribute type {"customeType"},
attribute email {"someaddress#domain.com"},
attribute origin {"eCommerce"},
attribute wishedChannel {"0"},
attribute externalId {"5515"}
)
into (/rtEvent)[1]')
SELECT #requestXML
it returns this:
<rtEvent type="customeType" email="someaddress#domain.com" origin="eCommerce" wishedChannel="0" externalId="5515">
<ctx>...</ctx>
</rtEvent>
Better use FOR XML PATH, which allows you to specify the naming and aliases as you like them:
SELECT 'SomeContext' AS [ctx]
FOR XML PATH('rtEvent')
This will return this:
<rtEvent>
<ctx>SomeContext</ctx>
</rtEvent>
But with the right attributes you get this:
SELECT 'customType' AS [#type]
,'someaddress#domain.com' AS [#email]
,'eCommerce' AS [#origin]
,0 AS [#wishedChannel]
,5515 AS [#externalId]
,'SomeContext' AS [ctx]
FOR XML PATH('rtEvent')
The result
<rtEvent type="customType" email="someaddress#domain.com" origin="eCommerce" wishedChannel="0" externalId="5515">
<ctx>SomeContext</ctx>
</rtEvent>
I have a table called XML (in SQL Server 2008) and it has a field called XmlDocument of type XML. I am trying to to delete an attribute from an XML variable.
Here is how my xml looks like
<clue_personal_auto xmlns="http://cp.com/rules/client">
<admin>
<receipt_date>03/16/2011</receipt_date>
<date_request_ordered>03/16/2011</date_request_ordered>
<report_usage>Personal</report_usage>
</admin>
</clue_personal_auto>
My query
UPDATE XML
SET XmlDocument.modify('delete (/clue_personal_auto/#xmlns)[1]')
WHERE xmlid = 357
When I run this query in query analyzer I see the message "1 row(s) affected" but in reality the xmlns attribute of clue_personal_auto element is not being removed. Any idea what am I doing wrong.
Thanks
BB
You need to use WITH xmlnamespaces, otherwise "/clue_personal_auto" does not match the NAMESPACED clue_personal_auto xmlns="..." node.
Not only that, you cannot actually remove a namespace since it is not a normal attribute.
Example of removing a regular attribute
declare #xml table (xmlid int, xmldocument xml)
insert #xml select 357, '
<clue_personal_auto xmlns="http://cp.com/rules/client" otherattrib="x">
<admin>
<receipt_date>03/16/2011</receipt_date>
<date_request_ordered>03/16/2011</date_request_ordered>
<report_usage>Personal</report_usage>
</admin>
</clue_personal_auto>'
;WITH XMLNAMESPACES ('http://cp.com/rules/client' as ns)
UPDATE #XML
SET XmlDocument.modify('delete (/ns:clue_personal_auto/#otherattrib)[1]')
WHERE xmlid = 357
select * from #xml
UPDATE XML
SET CONVERT(XML, REPLACE(CONVERT(NVARCHAR(MAX), XmlDocument), N' xmlns=...'))
WHERE ID = 357
I can't seem to find an easy way to do this - but the real question remains: why do you want to remove the namespace?? Using the WITH XMLNAMESPACES ... construct, you can easily make use of the namespaces.
Instead of putting a lot of effort in getting rid of it - learn about XML namespaces and start using them!
You can quite easily use that XML namespace in your queries:
;WITH XMLNAMESPACES (DEFAULT 'http://cp.com/rules/client' )
SELECT
XmlDocument.value('(/clue_personal_auto/admin/report_usage)[1]', 'varchar(25)')
FROM XML
WHERE ID = 357
and be happy with it - no need to artificially remove xmlns= declarations anymore!