There's already a few similar questions on here, but they don't seem to work for me. I need to query an XML file to extract data from it, so we can use the result of the query for other purposes. I have searched on the net for a method to do this, and it seems the value () method is the recommended method. I tried this with the following code:
DECLARE #x XML
SET #x =
'<?xml version="1.0" encoding="UTF-8"?>
<BlockOrderMessage xmlns="http://www.test.com/production/block"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.test.com/production/block file:/C:/Users/test.xsd"
BlockNumber="BlockNumber1">
<BlockStartTime>01:01:01.001</BlockStartTime>
<ProductionDate>2006-05-04</ProductionDate>
<BlockType>Bloc</BlockType>
<FlightOrders>
<FlightOrder>
<Flight>
<FlightNr>FlightNr0</FlightNr>
<DepartureDate>2006-05-04</DepartureDate>
<DepartureTime>01:01:01.001</DepartureTime>
<AircraftType>AircraftType0</AircraftType>
<AircraftSeatConfiguration>AircraftSeatConfiguration0</AircraftSeatConfiguration>
</Flight>
<CATC>CATC0</CATC>
</FlightOrder>
<FlightOrder>
<Flight>
<FlightNr>FlightNr1</FlightNr>
<DepartureDate>2006-05-04</DepartureDate>
<DepartureTime>01:01:01.001</DepartureTime>
<AircraftType>AircraftType1</AircraftType>
<AircraftSeatConfiguration>AircraftSeatConfiguration1</AircraftSeatConfiguration>
</Flight>
</FlightOrder>
</FlightOrders>
'
SELECT #x.value('(/BlockOrderMessage/Blocktype)[1]','int') AS 'BlockType',
#x.value('(/BlockOrderMessage/FlightOrders/FlightOrder/Flight/FlightNr)[1]','VARCHAR (20)') AS 'FlightNr',
#x.value('(/BlockOrderMessage/FlightOrders/FlightOrder/Flight/AircraftType)[1]','VARCHAR (20)') AS 'AircraftType'
Then i receive a query result of three columns (so far so good), but the records are NULL for all three columns. It does work when I remove the namespace declarations in BlockOrderMessage, so I assume I need to declare these namespaces somewhere in my query, but I cannot find how. Does anyone how I should do this?
Thanks in advance!
Hy,
First of all i think it is case sensitive, so correct Blocktype to BlockType. Do not convert BlockType to int because it is string so use nvarchar(max). Use xml namespaces in your query, like this
; WITH XMLNAMESPACES (default 'http://www.test.com/production/block')
SELECT #x.value('(/BlockOrderMessage/BlockType)[1]','varchar(max)') AS 'BlockType',
#x.value('(/BlockOrderMessage/FlightOrders/FlightOrder/Flight/FlightNr)[1]','VARCHAR (20)') AS 'FlightNr',
#x.value('(/BlockOrderMessage/FlightOrders/FlightOrder/Flight/AircraftType)[1]','VARCHAR (20)') AS 'AircraftType' `
Here is the full query
DECLARE #x XML
SET #x =
'<?xml version="1.0" encoding="UTF-8"?>
<BlockOrderMessage xmlns="http://www.test.com/production/block"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.test.com/production/block file:/C:/Users/test.xsd"
BlockNumber="BlockNumber1">
<BlockStartTime>01:01:01.001</BlockStartTime>
<ProductionDate>2006-05-04</ProductionDate>
<BlockType>Bloc</BlockType>
<FlightOrders>
<FlightOrder>
<Flight>
<FlightNr>FlightNr0</FlightNr>
<DepartureDate>2006-05-04</DepartureDate>
<DepartureTime>01:01:01.001</DepartureTime>
<AircraftType>AircraftType0</AircraftType>
<AircraftSeatConfiguration>AircraftSeatConfiguration0</AircraftSeatConfiguration>
</Flight>
<CATC>CATC0</CATC>
</FlightOrder>
<FlightOrder>
<Flight>
<FlightNr>FlightNr1</FlightNr>
<DepartureDate>2006-05-04</DepartureDate>
<DepartureTime>01:01:01.001</DepartureTime>
<AircraftType>AircraftType1</AircraftType>
<AircraftSeatConfiguration>AircraftSeatConfiguration1</AircraftSeatConfiguration>
</Flight>
</FlightOrder>
</FlightOrders>
</BlockOrderMessage>'
; WITH XMLNAMESPACES (default 'http://www.test.com/production/block')
SELECT #x.value('(/BlockOrderMessage/BlockType)[1]','varchar(50)') AS 'BlockType',
#x.value('(/BlockOrderMessage/FlightOrders/FlightOrder/Flight/FlightNr)[1]','VARCHAR (20)') AS 'FlightNr',
#x.value('(/BlockOrderMessage/FlightOrders/FlightOrder/Flight/AircraftType)[1]','VARCHAR (20)') AS 'AircraftType'
Related
i've been trying to import the field GivenName in my example XML but for some reason it's not working, i've been using the following SQL query, i think i'm using the correct field and the correct nodes but i'm not 100% sure about the XMLNameSpaces
Thank you very much in advance for your help
This is the example SQL Query i'm using:
DECLARE #xml XML = (SELECT [Xml] FROM ExampleTable)
;WITH XMLNAMESPACES (DEFAULT 'http://www.opentravel.org/OTA/2003/05','http://www.w3.org/2003/05/soap-envelope' )
select FirstName = ProfileInfo.value('Profiles[1]/ProfileInfo[1]/Profile[1]/Customer[1]/PersonName[1]/#GivenName', 'nvarchar(255)')
FROM #xml.nodes('Envelope/Body/OTA_Example/Info/Infos/ResUser') as T1(Profiles)
outer apply T1.Profiles.nodes('ResUser2') as T2(ProfileInfo)
This is the example XML i'm using for the import:
<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">
<soap2:Header xmlns:htng="http://htng.org/1.3/Header/" xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:wss="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:htnga="http://htng.org/PWSWG/2007/02/AsyncHeaders"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soap2="http://www.w3.org/2003/05/soap-envelope">
<wsa:Action>Example</wsa:Action>
<wsa:ReplyTo>
<wsa:Address>Example2</wsa:Address>
</wsa:ReplyTo>
<htnga:ReplyTo>
<wsa:Address>Example3</wsa:Address>
</htnga:ReplyTo>
<wsa:MessageID>123</wsa:MessageID>
</soap2:Header>
<Body>
<OTA_Example xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.opentravel.org/OTA/2003/05" Version="5.000" >
<Info>
<Infos CreateDateTime="2021-09-20T06:52:40" CreatorID="User">
<UniqueID Type="1" ID="12341251" />
<ResUser>
<ResUser2 ResGuestRPH="1" PrimaryIndicator="true">
<Profiles>
<ProfileInfo>
<Profile ProfileType="1">
<Customer>
<PersonName>
<NamePrefix>Mr.</NamePrefix>
<GivenName>FirstnameTest</GivenName>
<Surname>LastnameTest</Surname>
</PersonName>
</Customer>
</Profile>
</ProfileInfo>
</Profiles>
</ResUser2>
</ResUser>
</Infos>
</Info>
</OTA_Example>
</Body>
</Envelope>
GivenName is not an attribute, so you shouldn't use # for it.
It's unclear why you needed .nodes, it is only needed if there were multiple nodes that needed breaking out into separate rows
You can also select straight out of ExampleTable, you do not need to store it in a variable.
;WITH XMLNAMESPACES (
'http://www.w3.org/2003/05/soap-envelope' AS soap,
DEFAULT 'http://www.opentravel.org/OTA/2003/05')
select FirstName = [XML].value('(soap:Envelope/soap:Body/OTA_Example/Info/Infos/ResUser/ResUser2/Profiles/ProfileInfo/Profile/Customer/PersonName/GivenName/text())[1]', 'nvarchar(255)')
FROM ExampleTable
db<>fiddle
I have an XML in SQL table column. I need to decode this xml and get value of particular nodes. Find my XML below
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns:createTransactionResponse
xmlns:impl="http://office/work/services/service1"
xmlns:ns="http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd"
xmlns:tns="http://www.erdeftq.ae/Activematrix/ESB/service1/1_0">
<transactionResponse>
<transaction-info>
<registrationId>R1234</registrationId>
<trialId>T12345</trialId>
<transactionId>12345</transactionId>
<transactionDate>27-02-2020:08:47</transactionDate>
<status>Confirmed</status>
</transaction-info>
</transactionResponse>
</ns:createTransactionResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I need the values of nodes: registrationId, transactionId and status and I tried this but couldn't succeed as i got empty value as result:
DECLARE #xml XML
SET #xml = 'XML here'
SELECT T.C.value('#status', 'nvarchar(100)') FROM #xml.nodes('createTransactionResponse/transactionResponse/transaction-info/status') T(C)
SELECT T.C.value('#trans', 'nvarchar(100)') FROM #xml.nodes('createTransactionResponse/transactionResponse/transaction-info/transactionId') T(C)
SELECT T.C.value('#id', 'nvarchar(100)') FROM #xml.nodes('createTransactionResponse/transactionResponse/transaction-info/registrationId') T(C)
Any help/correction would be appreciated
Your own attempt is ignoring the namespaces and does not specify the full XPath.
Try one of these approaches:
Your XML:
DECLARE #xml XML
SET #xml = '<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns:createTransactionResponse
xmlns:impl="http://office/work/services/service1"
xmlns:ns="http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd"
xmlns:tns="http://www.erdeftq.ae/Activematrix/ESB/service1/1_0">
<transactionResponse>
<transaction-info>
<registrationId>R1234</registrationId>
<trialId>T12345</trialId>
<transactionId>12345</transactionId>
<transactionDate>27-02-2020:08:47</transactionDate>
<status>Confirmed</status>
</transaction-info>
</transactionResponse>
</ns:createTransactionResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
--This is the most explicit (which is always a best way):
WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS n1
,'http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd' AS n2)
SELECT #xml.value('(/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info/registrationId/text())[1]','nvarchar(max)') AS RegistrationId
,#xml.value('(/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info/transactionId/text())[1]','nvarchar(max)') AS TransactionId
,#xml.value('(/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info/status/text())[1]','nvarchar(max)') AS [Status];
--This will avoid some repeated XPath, but .nodes() produces quite some overhead:
WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS n1
,'http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd' AS n2)
SELECT ti.value('(registrationId/text())[1]','nvarchar(max)') AS RegistrationId
,ti.value('(transactionId/text())[1]','nvarchar(max)') AS TransactionId
,ti.value('(status/text())[1]','nvarchar(max)') AS [Status]
FROM #xml.nodes('/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info') A(ti);
--And this is for lazy people :-)
SELECT #xml.value('(//*:registrationId)[1]','nvarchar(max)') AS RegistrationId
,#xml.value('(//*:transactionId)[1]','nvarchar(max)') AS TransactionId
,#xml.value('(//*:status)[1]','nvarchar(max)') AS [Status];
Hint: The last one (for lazy people) uses the deep search (with //) and uses a wildcard for the namespace. This is very dangerous if the elements might occur more than once within your XML.
The XML you have is pretty complex. You have multiple namespaces, with different nodes using different ones. This means you need to use WITH XMLNAMESPACES to declare all these.
Then you need to use nodes to navigate the to needed node, prefixing them with the appropriate namespaces, till you get to transaction-info. Then you use use value to get the information.
#Status isn't what you're after here, that's for is you have something like <node status=1\>, you need to get the text() value of the node.
This results in the below:
DECLARE #X xml = '<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns:createTransactionResponse
xmlns:impl="http://traffic2/traffic/services/service1"
xmlns:ns="http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd"
xmlns:tns="http://www.abc.ae/Activematrix/ESB/service1/1_0">
<transactionResponse>
<transaction-info>
<registrationId>R1234</registrationId>
<trialId>T12345</trialId>
<transactionId>12345</transactionId>
<transactionDate>27-02-2020:08:47</transactionDate>
<status>Confirmed</status>
</transaction-info>
</transactionResponse>
</ns:createTransactionResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS [SOAP-ENV],
'http://traffic2/traffic/services/service1' AS impl, --YOu don't use this in the XML, but incldued anyway, for completeness
'http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd' AS ns,
'http://www.abc.ae/Activematrix/ESB/service1/1_0' AS tns) --YOu don't use this in the XML, but incldued anyway, for completeness
SELECT ti.value('(status/text())[1]','varchar(10)') AS [Status],
ti.value('(transactionId/text())[1]','int') AS Trans,
ti.value('(registrationId/text())[1]','varchar(10)') AS ID
FROM #X.nodes('SOAP-ENV:Envelope/SOAP-ENV:Body/ns:createTransactionResponse/transactionResponse/transaction-info') N(ti);
I have XML data in an NTEXT field (ou.ORDMODE), that I need to parse out a value (description) from. Column may contain null values. The data looks like this:
<?xml version="1.0" encoding="utf-16"?>
<UDFValidatedValue xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF">
<Description>Export</Description>
<Value>EXP</Value>
<ValueType>String</ValueType>
</UDFValidatedValue>
The line I have in my query is this:
CAST(REPLACE(CAST(ou.ORDMODE as NVARCHAR(MAX)),' xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF"','') as XML).value ('(/UDFValidatedValue/Description/text())[0]', 'nvarchar(100)') as Mode3,
but Mode3 column is returned as blank.
What am I doing wrong?
You could use this:
DEclare #xml xml = N'<?xml version="1.0" encoding="utf-16"?>
<UDFValidatedValue xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF">
<Description>Export</Description>
<Value>EXP</Value>
<ValueType>String</ValueType>
</UDFValidatedValue>';
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF')
select #xml.value ('(/UDFValidatedValue/Description)[1]', 'nvarchar(100)') as Mode3
or
select #xml.value ('declare namespace
udf="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF";
(/udf:UDFValidatedValue/udf:Description)[1]', 'nvarchar(100)') as Mode3
Demo link: Rextester
As ZLK pointed out in his comment, your own approach seems to be quite okay, just the index must be 1 instead of 0. Try it here:
ad "quite okay": Actually this is absolutely far away from "quite okay", read my hint below...
DECLARE #tbl TABLE(SomeXmlInText TEXT);
INSERT INTO #tbl VALUES
('<?xml version="1.0" encoding="utf-16"?>
<UDFValidatedValue xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF">
<Description>Export</Description>
<Value>EXP</Value>
<ValueType>String</ValueType>
</UDFValidatedValue>');
SELECT SomeXmlInText
,CAST(REPLACE(CAST(SomeXmlInText as NVARCHAR(MAX)),' xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF"','') as XML).value ('(/UDFValidatedValue/Description/text())[1]', 'nvarchar(100)') as Mode3
FROM #tbl
From your answer I take, that there is still no result...
Some possible issues:
XML is not really as you expect it
more / other namespaces
some weird no, not for you issue :-D
Hint
As you probably know TEXT, NTEXT and IMAGE are deprecated for centuries and should really not be used any more... If you have the slightest chance to change this, then you should store your XML in the appropritate XML-type...
You should not need any casts or manipulations on string level. This can be working, but is really out-dated...
I have to query an XML to extract the data and put it into columns. This works perfectly. However, I want to include a loop because the structure in the XML is as follows:
<BlockOrderMessage>
<FlightOrder>
<Flight>
<FlightNr>5</FlightNr>
<AircraftType>A255</AircraftType>
</Flight>
<PositionOrders>
<PositionOrder Unit="Unit 5">
<UnitName>UnitName5</UnitName>
<CardColor>Blue</CardColor>
</PositionOrder>
<PositionOrder Unit='Unit 6">
<UnitName>UnitName6</UnitName>
<CardColor>Red</CardColor>
</PositionOrder>
</PositionOrders>
</FlightOrder>
</BlockOrderMessage>
There's always only one , but there can be more ... Now, I can generate the column (when knowing the amount of ) using following code:
DECLARE #Data XML
SET #Data =
'<?xml version="1.0" encoding="UTF-8"?>
<BlockOrderMessage xmlns="http://www...."
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.... file:/C:/Users/.....xsd">
<FlightOrder>
<Flight>
<FlightNr>FlightNr0</FlightNr>
<AircraftType>AircraftType0</AircraftType>
<PositionOrders>
<PositionOrder Unit="Unit 5">
<UnitName>UnitName5</UnitName>
<CardColor>Blue</CardColor>
</PositionOrder>
<PositionOrder Unit="Unit 6">
<UnitName>UnitName6</UnitName>
<CardColor>Red</CardColor>
</PositionOrder>
</PositionOrders>
</FlightOrder>
</BlockOrderMessage>'
;WITH XMLNAMESPACES (DEFAULT 'http://www....')
SELECT #Data.value('(/BlockOrderMessage/FlightOrder/Flight/FlightNr)[1]','VARCHAR(20)') AS 'FlightNr',
#Data.value('(/BlockOrderMessage/FlightOrder/Flight/AircraftType)[1]','VARCHAR(20)') AS 'AircraftType',
#Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder/#Unit)[1]','VARCHAR(30)') AS 'PosOrder1_Unit ',
#Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder/UnitName)[1]','VARCHAR(30)') AS 'PosOrder1_UnitName',
#Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder/CardColor)[1]','VARCHAR(30)') AS 'PosOrder1_CardColor',
#Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder[2]/#Unit)[1]','VARCHAR(30)') AS 'PosOrder2_Unit',
#Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder[2]/UnitName)[1]','VARCHAR(30)') AS 'PosOrder2_UnitName',
#Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder[2]/CardColor)[1]','VARCHAR(30)') AS 'PosOrder2_CardColor'
But I want to insert a loop for the PositionOrder bit, I tried the following code (only the last part, since the rest stays the same):
;WITH XMLNAMESPACES (DEFAULT 'http://www....')
SELECT #Data.value('(/BlockOrderMessage/FlightOrder/Flight/FlightNr)[1]','VARCHAR(20)') AS 'FlightNr',
#Data.value('(/BlockOrderMessage/FlightOrder/Flight/AircraftType)[1]','VARCHAR(20)') AS 'AircraftType'
DECLARE #counter INT
SET #counter = 1
WHILE (#Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder/#Unit)[1]') IS NOT NULL)
BEGIN
;WITH XMLNAMESPACES (DEFAULT 'http://www....')
SELECT #Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder/#Unit)[1]','VARCHAR(30)') AS 'PosOrder_#counter_Unit ',
#Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder/UnitName)[1]','VARCHAR(30)') AS 'PosOrder_#counter_UnitName',
#Data.value('(/BlockOrderMessage/FlightOrder/PositionOrders/PositionOrder/CardColor)[1]','VARCHAR(30)') AS 'PosOrder_#counter_CardColor'
SET #counter = #counter +1
END
GO
Now, I have the following problems with this loop and the result of my query:
The results of the query for the PositionOrder are all NULL, this wasn't the case when I did it without loops.
I get two tables as query result, but I want them in one table. I tried to do it so I only use one SELECT statement, but I can't get the code right.
I want the name of the column to show the counter number: So if we are in the second loop, we want the name of the column to be PosOrder_2_..., now it shows PosOrder_#counter_...
Does anybody know what I am doing wrong or how I should fix this problem?
Thanks in advance!
Are you looking for something like this?
DECLARE #Flights XML = '<BlockOrderMessage>
<FlightOrder>
<Flight>
<FlightNr>5</FlightNr>
<AircraftType>A255</AircraftType>
</Flight>
<PositionOrders>
<PositionOrder Unit="Unit 5">
<UnitName>UnitName5</UnitName>
<CardColor>Blue</CardColor>
</PositionOrder>
<PositionOrder Unit="Unit 6">
<UnitName>UnitName6</UnitName>
<CardColor>Red</CardColor>
</PositionOrder>
</PositionOrders>
</FlightOrder>
</BlockOrderMessage>'
SELECT
FlightNr = FltOrder.value('(Flight/FlightNr)[1]', 'int'),
AircraftType = FltOrder.value('(Flight/AircraftType)[1]', 'varchaR(100)'),
PosOrderUnit = PosOrder.value('#Unit', 'varchar(50)'),
PosOrderUnitName = PosOrder.value('(UnitName)[1]', 'varchar(50)'),
PosOrderCardColor = PosOrder.value('(CardColor)[1]', 'varchar(50)')
FROM
#Flights.nodes('/BlockOrderMessage/FlightOrder') AS XTbl(FltOrder)
CROSS APPLY
FltOrder.nodes('PositionOrders/PositionOrder') AS XTbl2(PosOrder)
This would produce an output something like this:
Basically, it grabs the "base" data from the <BlockOrderMessage> / <FlightOrder> node and displays those in columns 1 & 2, and then it cross applies all subnodes <PositionOrders> / <PositionOrder> inside that <FlightOrder> node and extracts the remaining information from those subnodes (any number of them).
This question already has answers here:
SQL Server FOR XML Enclosing Element?
(2 answers)
Closed 7 years ago.
I have a Script, which returns a XML using FOR XML in SQL 2008. Is there any way to add the version and encoding information in the beginning of the output. Eventually, i am planning to save the output in a file.
For example, right now my output looks like this
<Agents>
<Agent id="1">
<Name>Mike</Name>
<Location>Sanfrancisco</Location>
</Agent>
<Agent id="2">
<Name>John</Name>
<Location>NY</Location>
</Agent>
</Agents>
I would like to append the line <?xml version="1.0" encoding="UTF-8"?> in the beginning of the Xml output
So i want the output something like
<?xml version="1.0" encoding="UTF-8"?>
<Agents>
<Agent id="1">
<Name>Mike</Name>
<Location>Sanfrancisco</Location>
</Agent>
<Agent id="2">
<Name>John</Name>
<Location>NY</Location>
</Agent>
As #gbn points out in another answer and on another question, "the XML data is stored internally as ucs-2", and SQL Server doesn't include it when producing the data. However, you can convert the XML to a string and append the XML declaration at the beginning manually. However, simply using UTF-8 in the declaration would be inaccurate. The Unicode string which SQL produces is in UCS-2. For example, this will fail:
SELECT CONVERT(xml,N'<?xml version="1.0" encoding="UTF-8"?>' + CONVERT(NVARCHAR(MAX),CONVERT(XML,N'<x>' + NCHAR(10176) + N'</x>')));
with error:
Msg 9402, Level 16, State 1, Line 1 XML parsing: line 1, character 38,
unable to switch the encoding
This, on the other hand, will work as expected:
SELECT CONVERT(xml,N'<?xml version="1.0" encoding="UCS-2"?>' + CONVERT(NVARCHAR(MAX),CONVERT(XML,N'<x>' + NCHAR(10176) + N'</x>')));
Here is code which will produce the full, declaration-laden XML string you seek for your example data:
DECLARE #Agents TABLE
(
AgentID int,
AgentName nvarchar(50),
AgentLocation nvarchar(100)
);
INSERT INTO #Agents (AgentID, AgentName, AgentLocation) VALUES (1, N'Mike', N'Sanfrancisco');
INSERT INTO #Agents (AgentID, AgentName, AgentLocation) VALUES (2, N'John', N'NY');
WITH BaseData AS
(
SELECT
(
SELECT
AgentID AS '#id',
AgentName AS 'Name',
AgentLocation AS 'Location'
FROM #Agents
FOR XML PATH('Agent'), ROOT('Agents'), TYPE
) AS AgentXML
), FullStringTable AS
(
SELECT
*,
'<?xml version="1.0" encoding="UCS-2"?>' +
CONVERT(nvarchar(max),AgentXML) AS FullString
FROM BaseData
)
SELECT
AgentXML AS OriginalXML,
FullString,
CONVERT(xml,FullString) AS FullStringConvertedToXML
FROM FullStringTable;
SQL Server internally always uses utf-16 ucs-2 so you could just append it like we did. That is, SQL Server would never generate anything with "utf-8".
Edit: after some digging:
http://www.devnewsgroups.net/group/microsoft.public.sqlserver.xml/topic60022.aspx
http://forums.asp.net/t/1455808.aspx
If you will not be attempting to manipulate the results as TSQL XML then the simplest thing to do will be create a varchar with the string you wish to append then add the XML to it using a CAST statemnt to convert the XML to varchar.
declare #testXML as XML
declare #testPrefix as varchar(255)
set #testPrefix = '<?xml version="1.0" encoding="UTF-8"?>'
set #testXML = '<Agents> <Agent id="1"> <Name>Mike</Name> <Location>Sanfrancisco</Location> </Agent> <Agent id="2"> <Name>John</Name> <Location>NY</Location> </Agent></Agents>'
select #testPrefix
Select #testXML
select #testPrefix + CAST(#testXML as varchar(max))