How do I use With XMLNamespaces to create custom name spaces in SQL? - sql-server

I want to be able to produce the following namesspaces and types for an XML root element
<BaseTransactionRequest xmlns="http://schemas.datacontract.org/2004/07/SomeCompany" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:type="AType">
Typically the first 2 (that is, not including i:type="AType") can be produced without issue (with some tradeoffs, when using custom namespaces we cant represent nulls using the xmlns:ni namespace etc)
So, the latter type is problematic. For a referesher,
the WITH XMLNAMESPACES fearure is used like below (FOR XML part omitted):
;WITH XMLNAMESPACES ('http://www.w3.org/2001/XMLSchema-instance' as i, DEFAULT 'http://schemas.datacontract.org/2004/07/SomeCompany',
A solution to overcome was to write XML "literally" using string concatenation. But I believe and hope FOR XML and this can be used together.
EDIT: First cut was added in a real rush. Apologies.
EDIT2: Dyslexic fix

Your question is not very clear... You might have a misconception about your i:type="AType". This is not a namespace (whatever a custom namespace is), but a normal attribute, named type living in your namespace i, which is declared at xmlns:i="blah".
Try this
WITH XMLNAMESPACES ('http://www.w3.org/2001/XMLSchema-instance' as i
,DEFAULT 'http://schemas.datacontract.org/2004/07/SomeCompany')
SELECT 'AType' AS [#i:type]
FOR XML PATH('BaseTransactionRequest');
The result is a self closing tag, declaring two namespaces and containing your attribute:
<BaseTransactionRequest xmlns="http://schemas.datacontract.org/2004/07/SomeCompany"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
i:type="AType" />

Related

Query .evtx converted to .xml

Having used evtx_dump.py to convert .evtx files to .xml i seek to learn how to query it using XQuery or whatever helps me datamine the document using BaseX.
At this point whatever i try i can only query the whole document using //Events
When i define a path such as //Events/Event/System/[EventID = '4688'] i get 0 results.
This first query is to simply track all specific EventID matching a specific value.
Being new to BaseX and XQuery i found the documentation hard to apply to this use case.
I looked for tools to help me build an XQuery to no avail.
BaseX has all index features enabled i could find.
Br,
Joris
When XQuery fails to return data you are expecting it is often caused by the presence XML namespaces.
The Microsoft XML event log uses a XML namespace on Event nodes and it is inherited by their children. This is the xmlns='http://schemas.microsoft.com/win/2004/08/events/event' you can see in the files. E.g
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Events><Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
<System><Provider Name='SideBySide'/><EventID Qualifiers='49409'>59</EventID><Version>0</Version>
...
Your XQuery must adjust for that. Either by saying any namespace is ok (using *:)
//*:System/[*:EventID = '4688']
or by explicitly specifing the expected namespaces.
declare namespace ns="http://schemas.microsoft.com/win/2004/08/events/event";
/Events/ns:Event/ns:System[ns:EventID= '4688' ]
See this similar issue xquery-not-working-with-namespaces

Include text in ForXMLPath query in SQL Server

I want to include a line (simple text) in ForXMLPath query as
<Cat>
but I am having difficulties.
When I try it brings in weird characters with it.
Please help.
Thanks.
select
'<Cat>'
I expect this
<Cat>
but it displays below
<Cat>
I must admit, that your question is not clear...
XML is not just some text with fancy extras, but a very strictly organised text based container for data.
A simple SELECT '<Cat>' would never return as <Cat> without a FOR XML somewhere in your query. So please show us a (reduced!) example of your full query and the expected output, best provided as MCVE (a stand-alone sample with DDL, sample data, own attempt and expected output).
Just some general remarks:
If you want to place <Cat> within your XML the whole output will be broken XML. This opening tag demands for a closing </Cat> (or - alternatively - a self-closing <Cat />)
Assumably you try to add out-written tags to your XML as you'd do it in XSLT, JS, ASP.Net or any other XML/HTML producing approach.
Assumably your solution will be a FOR XML PATH() approach without the need of an out-written tag within your XML.
Just to give you an idea:
SELECT 'test' AS [SomeElement] FOR XML PATH('SomeRowTag'),ROOT('SomeRootTag');
prouces this XML
<SomeRootTag>
<SomeRowTag>
<SomeElement>test</SomeElement>
</SomeRowTag>
</SomeRootTag>
If you want to add a <Cat> element you could use an XPath like here
SELECT 'test' AS [Cat/SomeElement] --<-- You can add nest-levels here!
FOR XML PATH('SomeRowTag'),ROOT('SomeRootTag');
The result
<SomeRootTag>
<SomeRowTag>
<Cat>
<SomeElement>test</SomeElement>
</Cat>
</SomeRowTag>
</SomeRootTag>

Create Assembly is successful, nothing found in sys.assembly_modules

First attempt at a CLR Integration, so I can use Regex tools.
I create the assembly:
CREATE ASSEMBLY SQLRegexTools
FROM 'D:\CODE\SQLRegexTools\SQLRegexTools\bin\Release\SQLRegexTools.dll'
This succeeds, and the assembly appears in SELECT # FROM sys.assemblies.
But there are no records returned to SELECT * FROM sys.Assembly_modules.
And when I try to CREATE FUNCTION to call one of the methods,
CREATE FUNCTION RegExMatch(#pattern varchar(max)
, #SearchIn varchar(max)
, #options int)
RETURNS varchar
EXTERNAL NAME SQLRegexTools.Functions.RegExMatch
I get an error 'Msg 6505, Could not find Type "Functions" in assembly 'SQLRegexTools.'
The class name of the VB module is "Functions". Why is this called a type in the error, and why might I not be seeing anything in the modules?
This error occurs when SQL Server is unable to resolve the name as provided. Try the snippet below to see if this resolves the issue.
CREATE FUNCTION RegExMatch(#pattern varchar(max)
, #SearchIn varchar(max)
, #options int)
RETURNS varchar
AS EXTERNAL NAME SQLRegexTools.[Functions].RegExMatch
The resolution of individual functions/methods for accessing native code is relatively indistinguishable from a multipart object name (e.g. a type).
An additional note is that native code that utilizing nested namespaces must fully qualify nested namespaces within the same pair of quotes. For example, if RegExMatch were located at SQLRegexTools.A.B.C.RegExMatch, you would reference this as SQLRegexTools.[A.B.C].RegExMatch when using it with EXTERNAL NAME in SQL Server.
That hits the answer Joey. Thank you.
There are several gotchas in this process I learned after much sleuthing and testing. They are often buried in long posts about the entire process, I will summarize here:
As Joey explained, the fully qualified name is needed. To be more complete...
Example:
1 2 3 4
[SQLRegexToolsASM].[SQLRegexToolsNS.RegexFunctionsClass].RegExMatch
This is the assembly name in the CREATE ASSEMBLY step.
This is the Root Namespace from the properties of the assembly project. If you declared a namespace or two, they must be included in their proper order, after this item and before item 3 in this list. Observe all these namespaces are enclosed in their own square brackets.
assembly.[rootnamespace.namespace.namespace].classname.methodnameā€
This is the Class Name you assigned to the class, maybe like this
Public Class RegexFunctionsClass
The name of the method defined in your VB or C# assembly.

XPath query: parsing multiple paths using the same query (Cross Apply / .nodes() )

I have a rather big and structured XML receipt which one I want to parse into a relational database. There are some equal structures on different levels, so it'd be very good to parse them using the same SQL statement. Like:
DECLARE #XMLPath varchar(127)
SET #XMLPath = 'atag/btag/item'
INSERT INTO XMLReadItems
SELECT ci.InvoiceID,
T.c.value('productname[1]', 'varchar(63)') AS InvoiceTarget,
T.c.value('unit[1]', 'varchar(15)') AS Unit,
FROM #XMLItems ci CROSS APPLY XMLCol.nodes(*[local-name()=sql:variable("#XMLPath")]') T(c)
Where #XMLPath could be a string from a variable or even a field from a table (what about using sql:column()?). But any of them I couldn't make work.
I can only use a static string in XMLCol.nodes().
There is no way you can construct XQuery parameter dynamically as it is limited to literal string only. See what MSDN says about the parameter of nodes() XML method :
XQuery
Is a string literal, an XQuery expression. If the query expression constructs nodes, these constructed nodes are exposed in the resulting rowset. If the query expression results in an empty sequence, the rowset will be empty. If the query expression statically results in a sequence that contains atomic values instead of nodes, a static error is raised.
Forcing to pass SQL variable to nodes() method would trigger error :
The argument 1 of the XML data type method "nodes" must be a string literal.
The trick you're trying to implement only works for matching element by name dynamically, not constructing the entire XPath dynamically. For example, the following should work fine to shred on item elements :
SET #elementName = 'item'
SELECT .....
FROM #XMLItems ci
CROSS APPLY XMLCol.nodes('//*[local-name()=sql:variable("#elementName")]') T(c)
In the end there is no workaround to this limitation as far as I can see, unless you want to go farther to construct the entire query dynamically (see: sp_executesql).

XQuery to get list of attributes

If I have several Section elements in an XML document, what XQuery do I use to get a list of all the name values?
<Section name="New Clients" filePath="XNEWCUST.TXT" skipSection="False">
In XPath 2.0 (which is a subset of XQuery) one would use the following expression to get a sequence of all string values of the "name" attributes of the "Section" elements:
for $attr in //Section/#name
return string($attr)
Do note that using the "//" abbreviation is typically a bad practice as this may require a whole (subtree) to be traversed. In any case where the structure of the document is known a more specific XPath expression (such as one using specific location steps) should be preferred.
//Section/#name
or
//Section/#name/string(.)
for the string values
/Section/#name

Resources