reading xml file with namespace - sql

we need to read a xml file in sql server but we are having problems because the xml have a namespace, I have tried several solutions but I can't resolve the problem.
the xml file looks like this
<?xml version="1.0" encoding="UTF-8"?>
<Status:orders xmlns:Status="http://www.test.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.test.com Status.xsd">
<order>
<Header>
<Name>500039</Name>
<Letter>A</Letter>
</Header>
</order>
</Status:orders>
can you help how to retrieve the values for the Name and letter tags
thanks in advance.

Your friend is called WITH XMLNAMESPACES...
Try it like this
DECLARE #xml XML=
'<?xml version="1.0" encoding="UTF-8"?>
<Status:orders xmlns:Status="http://www.test.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.test.com Status.xsd">
<order>
<Header>
<Name>500039</Name>
<Letter>A</Letter>
</Header>
</order>
</Status:orders>';
WITH XMLNAMESPACES('http://www.test.com' AS Status)
SELECT #xml.value('(/Status:orders/order/Header/Name)[1]','int')
,#xml.value('(/Status:orders/order/Header/Letter)[1]','varchar(max)');
An alternative was, to use the asterisk.
SELECT #xml.value('(/*:orders/order/Header/Name)[1]','int')
,#xml.value('(/*:orders/order/Header/Letter)[1]','varchar(max)');
Another alternative was this:
SELECT #xml.value('(//Name)[1]','int')
,#xml.value('(//Letter)[1]','varchar(max)');
But in general it is good advice, to be as specific as possible...

Related

How to get id field from http xml response in power automate

How to get id field from http xml response in power automate
<?xml version="1.0" encoding="UTF-8"?>
<ServiceResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://test.qg.apps.test.com/qps/xsd/3.0/was/report.xsd">
<responseCode>SUCCESS</responseCode>
<count>1</count>
<data>
<Report>
<id>3505012</id>
</Report>
</data>
</ServiceResponse>
how can i get outcome 3505012 in result
tried below
#{slice(outputs('createreportxml'), add(nthIndexOf(outputs('createreportxml'), '>', 9), 1), nthIndexOf(outputs('createreportxml'), '<', 10))}
as well as below
xpath(xml(outputs('#{outputs('createreportxml')}'))), 'string(/ServiceResponse/data/Report/id/text())'
sample flow
Got it working with below has small issue with one braces.
#{xpath(xml(outputs('createreportxml')),'string(/ServiceResponse/data/Report/id/text())')}

How to get child node value which has namespace on it?

I am completly new to XSLT and my goal is to retrieve value from a node which has namespace on it. My XML looks as below.
<WBIFNMsg>
<AppData>
<AppName>INFM</AppName>
<MsgType>Notification</MsgType>
<MsgStatus>Success</MsgStatus>
</AppData>
<AppMsg>
<Document xmlns="urn:swift:xsd:setr.010.001.03">
<SbcptOrdrV03>
<MsgId>
<Id>29331095XXXXML</Id>
<CreDtTm>2019-06-07T10:30:43.681+02:00</CreDtTm>
</MsgId>
</SbcptOrdrV03>
</Document>
</AppMsg>
</WBIFNMsg>
I am trying to get the value of Id tag which is under document/SbcptOrdrV03/MsgId/ via the xslt. As the document tag has the namespace I have included it in my xslt but still I couldn't retrieve the value.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:b="urn:swift:xsd:setr.010.001.03" exclude-result-prefixes="b">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="/">
<data>
<messageDataApps>
<field>
<fieldName>SENDER.STP</fieldName>
<value>
<xsl:value-of select="b:WBIFNMsg/b:AppMsg/b:Document/b:SbcptOrdrV03/b:MsgId/b:id"/>
</value>
</field>
</messageDataApps>
</data>
</xsl:template>
</xsl:stylesheet>
Appreciate if anyone could help on this. Thanks in advance.
Only Document and its descendants are in a namespace and require using a prefix when addressing them. Instead of:
<xsl:value-of select="b:WBIFNMsg/b:AppMsg/b:Document/b:SbcptOrdrV03/b:MsgId/b:id"/>
try:
<xsl:value-of select="WBIFNMsg/AppMsg/b:Document/b:SbcptOrdrV03/b:MsgId/b:Id"/>
Note also that XML is case-sensitive: b:id is not the same thing as b:Id.

Build New XML From Stored XML Value

We store rather large XML blobs (in an column of XML type) and I'm pursuing a skunkworks project to try to build up a subset of the XML on the fly when needed.
Let's say I have this XML blob stored in our database table in a given column:
<root>
<header>
<id>1</id>
<name id="foo">Name</name>
</header>
<body>
<items>
<addItem>
<val>1</val>
</addItem>
<observeItem>
<val>2</val>
</observeItem>
</items>
</body>
</root>
What I want to get out is this is to basically recreate the above document structure but only include one of the items children, so for example:
<root>
<header>
<id>1</id>
<name id="foo">Name</name>
</header>
<body>
<items>
<observeItem>
<val>2</val>
</observeItem>
</items>
</body>
</root>
If I were interested in just the observeItem record (the items element can have any number of children, but I'll only ever be interested in a single one of them).
I know I can do something like SELECT #XML.query('//items/child::*[2]') to get just a given child item, but how would I build up the full original document in a query with just one of those children?
I've come up with a solution, but I'm not entirely pleased with it:
DECLARE #XML XML = '
<root>
<header>
<id>1</id>
<name id="foo">Name</name>
</header>
<body>
<items>
<addItem>
<val>1</val>
</addItem>
<observeItem>
<val>2</val>
</observeItem>
</items>
</body>
</root>'
DECLARE #NthChild INT = 2
SELECT
#XML.query('//header'),
#XML.query('//items/child::*[sql:variable("#NthChild")]') AS 'items'
FOR XML PATH('root')
I don't like having to specify the root explicitly nor the items, but I think this approach could get me by.

SQL query for Dynamic nodes in XML [duplicate]

This question already has an answer here:
SQL Data as XML Element
(1 answer)
Closed 5 years ago.
Our table is like
StudentNo Name Subject Mark Grade
1 John English 41 A
1 John Hindi 42 B
We want an XML format from this table as follows.
<Student>
<Name>John</Name>
<Subject>
<English>
<Mark>41</Mark>
<Grade>A</Grade>
</English>
<Hindi>
<Mark>42</Mark>
<Grade>B</Grade>
</Hindi>
</Subject>
<Student>
Here the subject name nodes should be generated dynamically.
This is very similar to SQL Data as XML Element - so much so that I think it might be a duplicate - but I want to explain a bit more for your context why this isn't the best idea. In my answer to that question, I show a really hacky way that you could do this, but it's not the best idea.
Your XML will be nearly impossible to create a schema for. Any consumer of that XML will never be able to be sure what values might appear as elements. Rather than try to create dynamic elements, you should probably use attributes of some sort. You could even use xsi:type to create an abstract type in your XML of sorts (although in my example I'm just using a plain old attribute - you could pick whatever attribute will make the most sense for your consumers). The Query for that XML would be:
declare #subjects TABLE(studentno int, name varchar(10), subjecT varchar(10), mark int, grade char(1))
INSERT #subjects
VALUES
(1, 'John','English', 41,'A'),
(1, 'John','Hindi', 42,'B')
select
s.Name
,(SELECT
s2.Subject as '#type'
,s2.Mark
,s2.Grade
FROM #subjects s2
WHERE s2.studentno = s.studentno
FOR XML PATH('Subject'), ROOT('Subjects'), TYPE)
from #subjects s
GROUP BY s.name, s.studentno
FOR XML PATH('Student')
produces:
<Student>
<Name>John</Name>
<Subjects>
<Subject type="English">
<Mark>41</Mark>
<Grade>A</Grade>
</Subject>
<Subject type="Hindi">
<Mark>42</Mark>
<Grade>B</Grade>
</Subject>
</Subjects>
</Student>
This XML will be possible to make sense of by consumers, where they can, for example, iterate the subjects without knowing what subjects might be there (and without needing to resort to assuming that every direct child of Subjects is in fact a subject and not some other type of node that got added in a new version of the schema).
If you really need that output, I'd prefer to use XSLT to transform the output above to your format, e.g.:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="Subject">
<xsl:element name="{#type}">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Subjects">
<xsl:element name="Subject">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:transform>
gets you
<?xml version="1.0" encoding="UTF-8"?>
<Student>
<Name>John</Name>
<Subject>
<English>
<Mark>41</Mark>
<Grade>A</Grade>
</English>
<Hindi>
<Mark>42</Mark>
<Grade>B</Grade>
</Hindi>
</Subject>
</Student>
Note you can't do this completely with SQL Server though - you'd have to resort to building the XML string and casting it as XML, as in my other answer.

Add additional nodes in XML

I create an XML document using the below code
For Each cust As Customer In Customers
XDoc = <?xml version="1.0" encoding="UTF-16" standalone="yes"?>
<Customers>
<Customer>
<Name>Mike</Name>
<Age>0</Age>
</Customer>
</Customers>
Next
XDoc.Save("C:\myXmlfile.xml")
However it only seems to add one record, but i dont know how to readd the nodes for each record? So if theres 2 records then i would expect
<?xml version="1.0" encoding="UTF-16" standalone="yes"?>
<Customer>
<Name>Mike</Name>
<Age>0</Age>
</Customer>
<Customer>
<Name>Mike</Name>
<Age>0</Age>
</Customer>
but it should only generate one xml file.
Could anyone guide me in what i need to do please? Even after searching around im not 100% sure as theres too many methods and ive probably got confused.
Thanks
Inside the loop you set XDoc to the the customer. This means that when you save XDOC ONLY the last customer will be saved.
Even if you fix the above, you still have the issue that you are trying to add multiple root elements (each customer) to the XML document. This is not allowed, only one root element is allowed. So to produce a valid XML document, you want something like:
<Customers>
<Customer>
<Name>Mike</Name>
<Age>0</Age>
</Customer>
<Customer>
<Name>Mike</Name>
<Age>0</Age>
</Customer>
</Customers>
I'm sorry but I can recommend how to change your code, because I don't know the VB.Net extensions for XML. Hopefully, someone else will shed some light.
BTW, interesting handle.
You can generate XDocument and XElement separately.
Further, you can set data from variable using <%= %>.
Therefore, you should generate and combine like below.
' Generate customers
Dim customers As XEelement = <Customers></Customers>
For Each cust As Customer In Customers
customers.Add(
<Customer>
<Name><%= cust.Name %></Name>
<Age><%= cust.Age %></Age>
</Customer>
)
Next
' Combine customers
Dim XDoc As XDocument =
<?xml version="1.0" encoding="UTF-16" standalone="yes"?><%= customers %>
XDoc.Save("C:\myXmlfile.xml")
In addition to #RichardSchneider's answer, this is sample snippet in VB.
First, construct XDocument with single <Customers> element as root. Then in each iteration of For Each loop, add single <Customer> element to the root element of the XDocument :
XDoc As XDocument = <?xml version="1.0" encoding="UTF-16" standalone="yes"?><Customers></Customers>
For Each cust As Customer In customers
XDoc.Root.Add(
<Customer>
<Name>Mike</Name>
<Age>0</Age>
</Customer>
)
Next
XDoc.Save("C:\myXmlfile.xml")