SQL Output as XML stop repeating the namespace - sql-server

I'm using SSIS\SQL Server 2014 and am using the below SQL statement to output to XML.
In summary, 1 property can have many schedules. I only want the name space to appear once before the 'Job' XML path, however, if there are multiple schedules, then the namespace is also appearing in the xml output for each schedule - how do I stop this?
My code:
DECLARE #XMLOutput XML
DECLARE #XMLOutputChar nvarchar(max)
;WITH XMLNAMESPACES('http://www.example.com' as ns)
SELECT #XMLOutput =
(
SELECT
ISNULL(T1.[PropertyID],'') as PropertyId,
ISNULL(T1.[Desc],'') as JobDescription,
(
SELECT
ScheduleId as ScheduleId,
LocationID as LocationId,
FROM [JobSchedules]
WHERE T1.JobIdentity = JobIdentity
FOR XML PATH('JobSchedules'), TYPE
)
FROM [JobFile] T1
FOR XML PATH('Job'),TYPE,ROOT('JobLoader')
)
SET #XMLOutputChar = '<?xml version="1.0" encoding="UTF-8"?>' + CONVERT(nvarchar(max),#XMLOutput)
SELECT #XMLOutputChar AS XMLOutput
This is the desired output:
<?xml version="1.0" encoding="utf-8"?>
<JobLoader xmlns="http://www.example.com/" >
<Job>
<PropertyId>PROPERTYID</PropertyId>
<JobDescription>JOBDESCRIPTION</JobDescription>
<JobSchedules>
<JobSchedule>
<ScheduleId>SCHEDULEID</ScheduleId>
<LocationId>LOCATIONID</LocationId>
</JobSchedule>
</JobSchedules>
</Job>
</JobLoader>
What am I missing?

If anyone is interested I was able to resolve this by adding the below line to replace the value just after the encoding line:
SET #XMLOutputChar = REPLACE(#XMLOutputChar,'<JobSchedules xmlns:ns="http://www.example.com/"','<JobSchedules ')

Related

XML output in SQL help needed

Hi I have the following code in SQL that outputs xml between 2 tables. The output is 90% correct but if possible add to the output and then also remove some of the output text.
I am not sure if SQL has the ability to code a type of a element in the output. Please see the code below with the output. Currently if possible I would like to make 2 changes to my current output. The changes are list at the end of the port
DECLARE #ID_Rechnung int = 1978,
#XMLData xml;
WITH XMLNAMESPACES ('?xml version="1.0" encoding="UTF-8"?' as ext)
SELECT
#XMLData = xmldat.xmldataCol
FROM
(
SELECT (
SELECT
-- HIER XML Daten generieren
[InvoiceHeader].[InvoiceDate] AS 'invoice-date',
([InvoiceHeader].[InvoiceNumber]) AS 'invoice-number',
cast(replace([InvoiceHeader].[GrossValue],' ','') as decimal(18,2)) AS 'gross-total',
cast(replace([InvoiceHeader].[NetValue],' ','') as decimal(18,2)) AS 'amount-due',
[InvoiceHeader].[VatRate] AS 'tax-rate',
cast(replace([InvoiceHeader].[VatValue],' ','') as decimal(18,2)) AS 'tax-amount',
[ImagePath] AS 'image-scan-url',
[InvoiceType] AS 'document-type',
[LegalEntityVATNo] AS 'account-type/id',
[LegalEntityName] AS 'account-type/name',
[SupplierCode] as 'supplier/number',
[Currency] as 'currency/code',
(
SELECT rtrim([InvoiceLines].[LineNumber]) AS [order-line-num]
, [PONumber] as [po-number],
CAST([InvoiceLines].[UnitPrice] AS decimal(18,2)) AS Price ,
[Quantity] as quantity,
[TaxAmount] as [tax-amount],
[LineTotal] as [total],
[Decsription] as description
FROM [InvoiceLines] WHERE [InvoiceLines].[DOCID] = #id_Rechnung
FOR XML PATH('Invoice-line'), ROOT('invoice-lines'), TYPE
)
FROM [InvoiceHeader]
WHERE [InvoiceHeader].[DOCID] = #ID_Rechnung
FOR XML PATH(''), TYPE, ROOT('invoice-header')
) AS xmldataCol
) AS xmldat;
SELECT #XMLData
.query('<invoice-header>
{
for $x in /invoice-header/*[local-name()!="root"]
return $x,
for $x in /invoice-header/root/r
return <invoice-lines>/<invoice-line>{$x/*}</invoice-line></invoice-lines>
}
</invoice-header>');
Output:
<invoice-header>
<invoice-date>20180509</invoice-date>
<invoice-number>1075440</invoice-number>
<gross-total>1376.67</gross-total>
<amount-due>1197.10</amount-due>
<tax-rate>15.00%</tax-rate>
<tax-amount>179.57</tax-amount>
<image-scan-url>\\INTEL-SQL01\Attachment\2018-06-20\7e0dd165-81d6-445a-95d1-8aac686d44ed\f9a1179c-2a54-480e-b97a-ce6ac7327ae0.000</image-scan-url>
<account-type>
<id>4010112052</id>
<name>CONSOLIDATEDPOWERPROJECTS</name>
</account-type>
<supplier>
<number>12345</number>
</supplier>
<currency>
<code>ZAR</code>
</currency>
<invoice-lines xmlns:ext="?xml version="1.0" encoding="UTF-8"?">
<Invoice-line>
<order-line-num>4</order-line-num>
<po-number>120934861</po-number>
<Price>50.00</Price>
<quantity>1.000000</quantity>
<tax-amount>7.500000</tax-amount>
<total>50.00</total>
<description>Test1</description>
</Invoice-line>
<Invoice-line>
<order-line-num>2</order-line-num>
<po-number>120934861</po-number>
<Price>10.00</Price>
<quantity>2.000000</quantity>
<tax-amount>4.500000</tax-amount>
<total>20.00</total>
<description>Test2</description>
</Invoice-line>
</invoice-lines>
</invoice-header>
1.How do I get rid of the following xmlns:ext="?xml version="1.0" encoding="UTF-8&in the line: "<invoice-lines xmlns:ext="?xml version="1.0" encoding="UTF-8"?">"
How would I code "<tax-amount>7.500000</tax-amount> to get the output": "<tax-amount type="decimal">7.500000</tax-amount>"
Without a minimal reproducible example it is not possible to give you a full working answer.
(1) As #JeroenMostert already pointed out, the
'?xml version="1.0" encoding="UTF-8"?'
is an XML prolog declaration. Just delete the following line:
WITH XMLNAMESPACES ('?xml version="1.0" encoding="UTF-8"?' as ext)
(2) Here is a conceptual example how to add an attribute to an XML element. What is important here is a sequential order of adding, i.e. attribute shall be first, element itself is 2nd.
SQL
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, [tax-amount] VARCHAR(20));
INSERT INTO #tbl ([tax-amount]) VALUES
(N'7.500000'),
(N'18.000000');
SELECT
'decimal' AS [tax-amount/#type]
, [tax-amount]
FROM #tbl
FOR XML PATH('r'), TYPE, ROOT('root');
Output
<root>
<r>
<tax-amount type="decimal">7.500000</tax-amount>
</r>
<r>
<tax-amount type="decimal">18.000000</tax-amount>
</r>
</root>

Retrieving an XML node value with TSQL?

What am I not getting here? I can't get any return except NULL...
DECLARE #xml xml
SELECT #xml = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<webregdataResponse>
<result>0</result>
<regData />
<errorFlag>99</errorFlag>
<errorResult>Not Processed</errorResult>
</webregdataResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'
DECLARE #nodeVal int
SELECT #nodeVal = #xml.value('(errorFlag)[1]', 'int')
SELECT #nodeVal
Here is the solution:
DECLARE #xml xml
SELECT #xml = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<webregdataResponse>
<result>0</result>
<regData />
<errorFlag>99</errorFlag>
<errorResult>Not Processed</errorResult>
</webregdataResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'
declare #table table (data xml);
insert into #table values (#xml);
WITH xmlnamespaces (
'http://schemas.xmlsoap.org/soap/envelope/' as [soap])
SELECT Data.value('(/soap:Envelope/soap:Body/webregdataResponse/errorFlag)[1]','int') AS ErrorFlag
FROM #Table ;
Running the above SQL will return 99.
Snapshot of the result is given below,
That's because errorFlag is not the root element of your XML document. You can either specify full path from root element to errorFlag, for example* :
SELECT #nodeVal = #xml.value('(/*/*/*/errorFlag)[1]', 'int')
or you can use descendant-or-self axis (//) to get element by name regardless of it's location in the XML document, for example :
SELECT #nodeVal = #xml.value('(//errorFlag)[1]', 'int')
*: I'm using * instead of actual element name just to simplify the expression. You can also use actual element names along with the namespaces, like demonstrated in the other answer.

Casting a field to XML, querying it returns NULL records

I've a field on my table that is nvarchar(max) and contains XML document. Don't ask why it is nvarchar(max) instead of XML because I don't know it.
By the way, here is an extraction of a sample XML:
<?xml version="1.0" encoding="utf-16"?>
<ItemType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<AutoPay xmlns="urn:ebay:apis:eBLBaseComponents">true</AutoPay>
<Country xmlns="urn:ebay:apis:eBLBaseComponents">IT</Country>
<Currency xmlns="urn:ebay:apis:eBLBaseComponents">EUR</Currency>
<HitCounter xmlns="urn:ebay:apis:eBLBaseComponents">BasicStyle</HitCounter>
<ListingDuration xmlns="urn:ebay:apis:eBLBaseComponents">GTC</ListingDuration>
<ListingType xmlns="urn:ebay:apis:eBLBaseComponents">FixedPriceItem</ListingType>
<Location xmlns="urn:ebay:apis:eBLBaseComponents">Italy</Location>
<PaymentMethods xmlns="urn:ebay:apis:eBLBaseComponents">PayPal</PaymentMethods>
<PayPalEmailAddress xmlns="urn:ebay:apis:eBLBaseComponents">email#paypal.com</PayPalEmailAddress>
<PrimaryCategory xmlns="urn:ebay:apis:eBLBaseComponents">
<CategoryID>137084</CategoryID>
</PrimaryCategory>
<ShippingDetails xmlns="urn:ebay:apis:eBLBaseComponents">
<ShippingServiceOptions>
<ShippingService>StandardShippingFromOutsideUS</ShippingService>
<ShippingServiceCost currencyID="EUR">0</ShippingServiceCost>
<ShippingServiceAdditionalCost currencyID="EUR">0</ShippingServiceAdditionalCost>
<FreeShipping>true</FreeShipping>
</ShippingServiceOptions>
<InternationalShippingServiceOption>
<ShippingService>StandardInternational</ShippingService>
<ShippingServiceCost currencyID="EUR">0</ShippingServiceCost>
<ShippingServiceAdditionalCost currencyID="EUR">0</ShippingServiceAdditionalCost>
<ShippingServicePriority>1</ShippingServicePriority>
<ShipToLocation>Americas</ShipToLocation>
<ShipToLocation>Europe</ShipToLocation>
</InternationalShippingServiceOption>
<ShippingType>Flat</ShippingType>
<InsuranceDetails>
<InsuranceFee currencyID="EUR">0</InsuranceFee>
<InsuranceOption>NotOffered</InsuranceOption>
</InsuranceDetails>
<InternationalInsuranceDetails>
<InsuranceFee currencyID="EUR">0</InsuranceFee>
<InsuranceOption>NotOffered</InsuranceOption>
</InternationalInsuranceDetails>
</ShippingDetails>
<Site xmlns="urn:ebay:apis:eBLBaseComponents">US</Site>
<Storefront xmlns="urn:ebay:apis:eBLBaseComponents">
<StoreCategoryID>2947535016</StoreCategoryID>
<StoreCategory2ID>0</StoreCategory2ID>
</Storefront>
<DispatchTimeMax xmlns="urn:ebay:apis:eBLBaseComponents">4</DispatchTimeMax>
<ReturnPolicy xmlns="urn:ebay:apis:eBLBaseComponents">
<ReturnsAcceptedOption>ReturnsAccepted</ReturnsAcceptedOption>
<Description>Accepted</Description>
<ShippingCostPaidByOption>Buyer</ShippingCostPaidByOption>
</ReturnPolicy>
<ConditionID xmlns="urn:ebay:apis:eBLBaseComponents">1000</ConditionID>
</ItemType>
I would love to query the table on that field, for example to extract CategoryID field.
I tried everything I knew, like casting to ntext, removing utf-16, replacing it with utf-8, adding namespaces and stuff like that, but the result is always a NULL record.
Here is one of the queries I tried:
;WITH XMLNAMESPACES('urn:ebay:apis:eBLBaseComponents' AS ns,
'http://www.w3.org/2001/XMLSchema-instance' as xsi,
'http://www.w3.org/2001/XMLSchema' as xsd)
select CategoryVal = CONVERT(xml, [Template]).value('(/ItemType/PrimaryCategory/CategoryID)[1]', 'nvarchar(max)') FROM Templates where ID = 1
Thanks, Marco
with xmlnamespaces('urn:ebay:apis:eBLBaseComponents' as n)
select cast(Template as xml).value('(/ItemType/n:PrimaryCategory/n:CategoryID)[1]', 'nvarchar(max)')
from Templates
where ID = 1
You need to prefix the elements in your xpath expression.
I've done that once, but without the use of namespaces.
I had my input as varchar(max) (nvarchar should work too)
#Text AS varchar(MAX)
Then i used an XML type variable and the conversion was as simple as this:
DECLARE #XML XML
SELECT #XML = #Text
To query your CategoryID value you would use:
SELECT itemtype.item.value('(/ItemType/PrimaryCategory/CategoryID)[1]', 'nvarchar(max)')
FROM #XML.nodes('/ItemType') AS itemtype(item);

Concatenate XML without type casting to string

I have the following XML generated from various tables in my SQL SERVER database
<XMLData>
...
<Type>1</Type>
...
</XMLData>
AND
<XMLData>
...
<Type>2</Type>
...
</XMLData>
AND
<XMLData>
...
<Type>3</Type>
...
</XMLData>
The final output I need is single combined as follows:
<AllMyData>
<XMLData>
...
<Type>1</Type>
...
</XMLData>
<XMLData>
...
<Type>2</Type>
...
</XMLData>
<XMLData>
...
<Type>3</Type>
...
</XMLData>
<AllMyData>
NOTE - all the independent elements that I am combining have the same tag name.
Thanks in advance for looking this up.
I have the following XML generated from various tables in my SQL
SERVER database
Depends on how you have it but if it is in a XML variable you can do like this.
declare #XML1 xml
declare #XML2 xml
declare #XML3 xml
set #XML1 = '<XMLData><Type>1</Type></XMLData>'
set #XML2 = '<XMLData><Type>2</Type></XMLData>'
set #XML3 = '<XMLData><Type>3</Type></XMLData>'
select #XML1, #XML2, #XML3
for xml path('AllMyData')
I can't comment but can answer so even though I think a comment is more appropriate, I'll expand on what rainabba answered above to add a bit more control. My .Net code needs to know the column name returned so I can't rely on auto-generated names but needed the very tip rainabba provided above otherwise.
This way, the xml can effectively be concatenated into a single row and the resulting column named. You could use this same approach to assign the results to an XML variable and return that from a PROC also.
SELECT (
SELECT XmlData as [*]
FROM
(
SELECT
xmlResult AS [*]
FROM
#XmlRes
WHERE
xmlResult IS NOT NULL
FOR XML PATH(''), TYPE
) as DATA(XmlData)
FOR XML PATH('')
) as [someColumnName]
If you use for xml type, you can combine the XML columns without casting them. For example:
select *
from (
select (
select 1 as Type
for xml path(''), type
)
union all
select (
select 2 as Type
for xml path(''), type
)
union all
select (
select 3 as Type
for xml path(''), type
)
) as Data(XmlData)
for xml path(''), root('AllMyData'), type
This prints:
<AllMyData>
<XmlData>
<Type>1</Type>
</XmlData>
<XmlData>
<Type>2</Type>
</XmlData>
<XmlData>
<Type>3</Type>
</XmlData>
</AllMyData>
As an addendum to Mikael Eriksson's answer - If you have a process where you need to continually add nodes and then want to group that under a single node, this is one way to do it:
declare #XML1 XML
declare #XML2 XML
declare #XML3 XML
declare #XMLSummary XML
set #XML1 = '<XMLData><Type>1</Type></XMLData>'
set #XMLSummary = (SELECT #XMLSummary, #XML1 FOR XML PATH(''))
set #XML2 = '<XMLData><Type>2</Type></XMLData>'
set #XMLSummary = (SELECT #XMLSummary, #XML2 FOR XML PATH(''))
set #XML3 = '<XMLData><Type>3</Type></XMLData>'
set #XMLSummary = (SELECT #XMLSummary, #XML3 FOR XML PATH(''))
SELECT #XMLSummary FOR XML PATH('AllMyData')
I needed to do the same but without knowing how many rows/variables were concerned and without extra schema added so here was my solution. Following this pattern, I can generate as many snippets as I want, combine them, pass them between PROCS or even return them from procs and at any point, wrap them up in containers all without modifying the data or being forced to add XML structure into my data. I use this approach with HTTP end points to provide XML Web services and with another trick that converts XML into JSON, to provide JSON WebServices.
-- SETUP A type (or use this design for a Table Variable) to temporarily store snippets into. The pattern can be repeated to pass/store snippets to build
-- larger elements and those can be further combined following the pattern.
CREATE TYPE [dbo].[XMLRes] AS TABLE(
[xmlResult] [xml] NULL
)
GO
-- Call the following as much as you like to build up all the elements you want included in the larger element
INSERT INTO #XMLRes ( xmlResult )
SELECT
(
SELECT
'foo' '#bar'
FOR XML
PATH('SomeTopLevelElement')
)
-- This is the key to "concatenating" many snippets into a larger element. At the end of this, add " ,ROOT('DocumentRoot') " to wrapp them up in another element even
-- The outer select is a time from user2503764 that controls the output column name
SELECT (
SELECT XmlData as [*]
FROM
(
SELECT
xmlResult AS [*]
FROM
#XmlRes
WHERE
xmlResult IS NOT NULL
FOR XML PATH(''), TYPE
) as DATA(XmlData)
FOR XML PATH('')
) as [someColumnName]
ALTER PROCEDURE usp_fillHDDT #Code int
AS
DECLARE #HD XML,#DT XML;
SET NOCOUNT ON;
select invhdcode, invInvoiceNO,invDate,invCusCode,InvAmount into #HD
from dbo.trnInvoiceHD where invhdcode=#Code
select invdtSlNo No,invdtitemcode ItemCode,invdtitemcode ItemName,
invDtRate Rate,invDtQty Qty,invDtAmount Amount ,'Kg' Unit into #DT from
dbo.trnInvoiceDt where invDtTrncode=#Code
set #HD = (select * from #HD HD FOR XML AUTO,ELEMENTS XSINIL);
set #DT = (select* from #DT DT FOR XML AUTO,ELEMENTS XSINIL);
SELECT CAST ('<OUTPUT>'+ CAST (ISNULL(#HD,'') AS VARCHAR(MAX))+ CAST ( ISNULL(#DT,'') AS VARCHAR(MAX))+ '</OUTPUT>' AS XML)
public String ReplaceSpecialChar(String inStr)
{
inStr = inStr.Replace("&", "&");
inStr = inStr.Replace("<", "<");
inStr = inStr.Replace(">", ">");
inStr = inStr.Replace("'", "'");
inStr = inStr.Replace("\"", """);
return inStr;
}

Read Xml on SQL Server with namespace

I trying to read a XML file in SQL Server using this guide.
http://blog.sqlauthority.com/2009/02/13/sql-server-simple-example-of-reading-xml-file-using-t-sql/
Works fine, but I have a XML file with a namespace and doesn´t works this code with namespace.
Some solution?
Thanks Remus. Now I have this code
DECLARE #MyXML XML
SET #MyXML = '<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" Moneda="USD">
<cfdi:Impuestos totalImpuestosRetenidos="0.00" totalImpuestosTrasladados="1143.06">
</cfdi:Impuestos>
</cfdi:Comprobante> '
;with xmlnamespaces('http://www.sat.gob.mx/cfd/3' as cfdi)
SELECT
a.b.value('#Moneda','varchar(100)') Moneda,
a.b.value('Impuestos[1]/#totalImpuestosTrasladados','float') totalImpuestosTraslados
FROM #MyXML.nodes('cfdi:Comprobante') a(b)
Works, but the value of totalImpuestosTraslados is NULL.
Moneda totalImpuestosTraslados
USD NULL
Use WITH XMLNAMESPACES. Post details (what you tried, what error you got) if this information is insufficient.
DECLARE #MyXML XML
SET #MyXML = '<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" Moneda="USD">
<cfdi:Impuestos totalImpuestosRetenidos="0.00" totalImpuestosTrasladados="1143.06">
</cfdi:Impuestos>
</cfdi:Comprobante> '
;with xmlnamespaces('http://www.sat.gob.mx/cfd/3' as cfdi)
SELECT
a.b.value('#Moneda','varchar(100)') Moneda,
a.b.value('cfdi:Impuestos[1]/#totalImpuestosTrasladados','float') totalImpuestosTraslados
FROM #MyXML.nodes('cfdi:Comprobante') a(b)
"Impuestos" also have cfdi as namespace, so you have to include it too
DECLARE #MyXML XML
SET #MyXML = '<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" Moneda="USD">
<cfdi:Impuestos totalImpuestosRetenidos="0.00" totalImpuestosTrasladados="1143.06">
</cfdi:Impuestos>
</cfdi:Comprobante> '
;with xmlnamespaces('http://www.sat.gob.mx/cfd/3' as cfdi)
SELECT
a.b.value('#Moneda','varchar(100)') Moneda,
a.b.value('<b>/cfdi:</b>Impuestos[1]/#totalImpuestosTrasladados','float') totalImpuestosTraslados
FROM #MyXML.nodes('cfdi:Comprobante') a(b)

Resources