Search for multiple values in an xml column - sql

Environment: SQL Server 2012. Primary and secondary (value) index is built on xml column.
Say I have a table Message with xml column WordIndex. I also have a table Word which has WordId and WordText. Xml for Message.WordIndex has the following schema:
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.com">
<xs:element name="wi">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="w">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="p" type="xs:unsignedByte" />
</xs:sequence>
<xs:attribute name="wid" type="xs:unsignedByte" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
and some data to go with it:
<wi xmlns="http://www.example.com">
<w wid="1">
<p>28</p>
<p>72</p>
<p>125</p>
</w>
<w wid="4">
<p>89</p>
</w>
<w wid="5">
<p>11</p>
</w>
</wi>
I need to search for multiple values in my xml column WordIndex either using OR or AND. What I'm doing is fairly rudimentary, since I'm a n00b in XQuery (taken from debug output, hence real values):
with xmlnamespaces(default 'http://www.example.com')
select
m.Subject,
m.MessageId,
m.WordIndex.query('
let $dummy := 0
return
<word_list>
{
for $w in /wi/w
where $w/#wid=64
return <word wid="64" pos="{data($w/p)}"/>
}
{
for $w in /wi/w
where $w/#wid=70
return <word wid="70" pos="{data($w/p)}"/>
}
{
for $w in /wi/w
where $w/#wid=63
return <word wid="63" pos="{data($w/p)}"/>
}
</word_list>
') as WordPosition
from
Message as m
-- more joins go here ...
where
-- more conditions go here ...
and m.WordIndex.exist('/wi/w[#wid=64]') = 1
and m.WordIndex.exist('/wi/w[#wid=70]') = 1
and m.WordIndex.exist('/wi/w[#wid=63]') = 1
How can this be optimized?

Not sure I understand what your expected results are, but you could make this a bit more generic and data-driven (eg using sql:column or sql:variable). Try something like this:
declare #wids table ( wid INT PRIMARY KEY )
insert into #wids ( wid )
values ( 64 ), ( 70 )
;with xmlnamespaces(default 'http://www.example.com')
select
m.Subject,
m.MessageId,
m.WordIndex.query('
return
<word_list>
{
for $w in /wi/w
where $w/#wid = sql:column("w.wid")
return <word wid="{$w/#wid}" pos="{data($w/p)}"/>
}
</word_list>
') as WordPosition
from
Message as m
cross apply #wids w
where m.WordIndex.exist('wi/w[#wid=sql:column("w.wid")]') = 1

Related

XPATH assertion in xsd to check value of an attribute of first and last element of a complex type

I have the following xsd model and I need to have assertions to check on the type of first and last element of kPartsList : last part must be castOffPartPoint and first part must be castOnPartSeg;
I am using Python Package xmlSchema to validate the xml document
<?xml version="1.1" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace = "http://www.ludd21.com/kPartModel"
xmlns = "http://www.ludd21.com/kPartModel"
elementFormDefault="qualified"
vc:minVersion = "1.1"
xpathDefaultNamespace="##targetNamespace"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
>
<xs:element name="kPartModel">
<xs:complexType>
<xs:sequence>
<xs:element ref="kPartsPiece" minOccurs="1" maxOccurs = "unbounded"/>
</xs:sequence>
<xs:attribute name="modelName" type="xs:NCName" use = "required"/>
<xs:attribute name= "otherattribute" type="xs:string" default = ""/>
</xs:complexType>
<!--piecename must be unique within kpModel-->
<xs:unique name= "kPartModel">
<xs:selector xpath="*"/>
<xs:field xpath= "#pieceName"/>
</xs:unique>
</xs:element>
<xs:element name="kPartsPiece">
<xs:complexType>
<xs:sequence>
<xs:element ref= "kPartsList"/>
</xs:sequence>
<xs:attribute name="pieceName" type="xs:NCName"/>
<!-- #partNumber is unique across kPartsList -->
<xs:unique id = "unique-partNumber" name= "unique-partNumber">
<xs:selector xpath="*/*"/>
<xs:field xpath= "#partNumber"/>
</xs:unique>
</xs:element>
</xs:complexType>
<xs:element name = "kPartsList" >
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs= "0" maxOccurs = "unbounded">
<xs:element ref = "castOnPartSeg" />
<xs:element ref = "joinPart"/>
<xs:element ref = "castOffPartPoint"/>
</xs:choice>
</xs:sequence>
<!-- last part must be castOff -->
<xs:assert id = "test-end-castOff" test = "(kPartsList[last()]/#type = 'castOffPartPoint') or (kPartsList[last()]/#type = 'castOffPartSeg')"/>
<!-- first part must be castOn-->
<xs:assert id = "test-first-castOn" test = "if (/#partNumber= '0') then #type = 'castOnPartSeg' else false()"/>
</xs:complexType>
</xs:element>
<xs:element name="castOffPartPoint">
<xs:complexType>
<xs:sequence>
<xs:element ref ="basePoint"/>
</xs:sequence>
<xs:attribute name ="nextsnum" type = "kpRefsList" use = "required"/>
<xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
</xs:complexType>
</xs:element>
<xs:element name="castOnPartSeg">
<xs:complexType>
<xs:sequence>
<xs:element ref ="baseSeg"/>
</xs:sequence>
<xs:attribute name = "nextsnum" type = "kpRefsList" use = "required"/>
<xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
</xs:complexType>
</xs:element>
<xs:element name="joinPart">
<xs:complexType>
<xs:sequence>
<xs:element ref ="baseSeg"/>
</xs:sequence>
<xs:attribute name ="nextsnum" type = "kpRefsList" use = "required"/>
<xs:attribute name ="previousnum" type = "kpRefsList" use = "required"/>
<xs:attribute name = "partNumber" type = "xs:nonNegativeInteger" use = "required"/>
</xs:complexType>
</xs:element>
<xs:simpleType name = "kpRefsList">
<xs:list itemType= "xs:nonNegativeInteger"/>
</xs:simpleType>
<xs:element name = "basePoint">
<xs:complexType>
<xs:attribute name= "start" type = "point" use = "required"/>
</xs:complexType>
</xs:element>
<xs:element name = "baseSeg">
<xs:complexType>
<xs:attribute name= "start" type = "point" use = "required"/>
<xs:attribute name= "end" type = "point" use = "required"/>
<!--startY = endY!-->
<xs:assert id = "test_base_horizontal" test = "/#start[2] = /#end[2]"/>
</xs:complexType>
</xs:element>
<xs:simpleType name= "point">
<xs:restriction>
<xs:simpleType>
<xs:list itemType = "decimal5digits"/>
</xs:simpleType>
<xs:length value = "2"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name ="decimal5digits">
<xs:restriction base = "xs:decimal">
<xs:fractionDigits value="5"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
both assertions above always return false.
It should return true for this xml
<ns0:kPartModel xmlns:ns0="http://www.ludd21.com/kPartModel" modelName="manysingulars" otherattribute="">
<ns0:kPartsPiece pieceName="pieceknit_layer1">
<ns0:kPartsList>
<ns0:castOnPartSeg nextsnum="1" partNumber="0">
<ns0:baseSeg start="9.73143 0.00000" end="17.73188 0.00000" />
</ns0:castOnPartSeg>
<ns0:joinPart nextsnum="2" previousnum="0 2" partNumber="1" >
<ns0:baseSeg start="9.88173 -11.82912" end="25.27907 -11.82912" />
</ns0:joinPart>
<ns0:castOffPartPoint partNumber="2">
<ns0:basePoint start="21.83789 -8.02031"/>
</ns0:castOffPartPoint>
</ns0:kPartsList>
</ns0:kPartsPiece>
</ns0:kPartModel>

Dataset deserialization to object issue - vb .net

Hi everyone i'm having some trouble deserializing a dataset (with parent table) to the correspondent object.
Every table have is array object inside his parent object for deserialization, every array have default {}, but if parent table have no rows the inner object is set to nothing, not empty array... how may i correct this issue?!?
Thanks a lot to everyone.
VB .Net code:
Public Function DeserializeDataSetToObj(ByRef DataSetIn As DataSet, ByVal t As Type) As Object
Dim obj As Object = Nothing
Dim stream As MemoryStream = New MemoryStream()
Using writer As XmlWriter = XmlWriter.Create(stream)
DataSetIn.WriteXml(writer)
End Using
stream.Seek(0, SeekOrigin.Begin)
Using reader As XmlReader = XmlReader.Create(stream)
Try
Dim xmlSer As System.Xml.Serialization.XmlSerializer = New System.Xml.Serialization.XmlSerializer(t)
obj = xmlSer.Deserialize(reader)
Catch ex As Exception
End Try
End Using
Return obj
End Function
Objects Class
<Serializable(), XmlRoot("MainDataset"), XmlType("MainDataset")>
Public Class MainDataset
<XmlElement("Header1")>
Public Property Header1() as HeaderType
<XmlElement("Header2")>
Public Property Header2() as HeaderType
End Class
<Serializable()>
Public Class HeaderType
<XmlElement("Rows")>
Public Property Rows() as RowType
End Class
<Serializable()>
Public Class RowType
<XmlElement("ColumnA")>
Public Property ColumnA as String
<XmlElement("ColumnB")>
Public Property ColumnB as String
<XmlElement("ColumnC")>
Public Property ColumnC as String
End Class
XSD:
<?xml version="1.0" standalone="yes"?>
<xs:schema id="MainDataset" xmlns="" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element msdata:UseCurrentLocale="true" name="MainDataset">
<xs:complexType>
<xs:sequence>
<xs:element name="Header1">
<xs:complexType>
<xs:sequence>
<xs:element name="Rows">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="ColumnA" type="xs:string"/>
<xs:element minOccurs="0" name="ColumnB" type="xs:string"/>
<xs:element minOccurs="0" name="ColumnC" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Header2">
<xs:complexType>
<xs:sequence>
<xs:element name="Rows">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="ColumnA" type="xs:string"/>
<xs:element minOccurs="0" name="ColumnB" type="xs:string"/>
<xs:element minOccurs="0" name="ColumnC" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Problem solved... if i use list instead array i receive an empty list not a nothing object.
Thanks to everyone

SQL : How can I extract substring what I want from a long xml string?

I have a column which includes an XML value in my SQL table.
I have to read the values in these three elements:
<POSTN_ID>0000-0000H1-POS</POSTN_ID>
<ROLE_ID>0000-00002B-ROL</ROLE_ID>
<STATUS>1</STATUS>
Which SQL methods can help me in this situation?
<parameter name="OrgHierarchyDatasets_diffgram">
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<NewDataSet>
<PositionRoles diffgr:id="PositionRoles1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<POSTN_ID>0000-0000H1-POS</POSTN_ID>
<ROLE_ID>0000-00002B-ROL</ROLE_ID>
<STATUS>1</STATUS>
</PositionRoles>
</NewDataSet>
</diffgr:diffgram>
</parameter>
<parameter name="ExistNodeCheck" type="System.Boolean">True</parameter>
The following syntax can help you, capture the objects you define and take them to a table model.
DECLARE #xml xml = '
<parameter name="OrgHierarchyDatasets_diffgram">
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<NewDataSet>
<PositionRoles diffgr:id="PositionRoles1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<POSTN_ID>0000-0000H1-POS</POSTN_ID>
<ROLE_ID>0000-00002B-ROL</ROLE_ID>
<STATUS>1</STATUS>
</PositionRoles>
</NewDataSet>
</diffgr:diffgram>
</parameter>
<parameter name="ExistNodeCheck" type="System.Boolean">True</parameter>'
SELECT
Tbl.Col.value('POSTN_ID[1]', 'nvarchar(50)') AS POSTN_ID,
Tbl.Col.value('ROLE_ID[1]', 'nvarchar(50)') AS ROLE_ID,
Tbl.Col.value('STATUS[1]', 'nvarchar(50)') AS STATUS
FROM #xml.nodes('//PositionRoles') Tbl(Col)
Use xPath/xQuery:
select cast('<parameter name="OrgHierarchyDatasets_diffgram">
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<NewDataSet>
<PositionRoles diffgr:id="PositionRoles1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<POSTN_ID>0000-0000H1-POS</POSTN_ID>
<ROLE_ID>0000-00002B-ROL</ROLE_ID>
<STATUS>1</STATUS>
</PositionRoles>
</NewDataSet>
</diffgr:diffgram>
</parameter>
<parameter name="ExistNodeCheck" type="System.Boolean">True</parameter>' as xml) as col
into #tab
go
select
col,
nod,
nod.query('./POSTN_ID/text()'),
nod.value('./ROLE_ID[1]','varchar(255)')
from (
select
*, col.query('//PositionRoles/node()') as nod
from #tab
) x

spyne generating invalid schema

I am trying to use spyne from master branch as the released versions are not compatible with python3 and I have models defined like these:
class currency(ComplexModel):
data = XmlData(Decimal)
class mntCurrency(currency):
code = XmlAttribute(String)
class CreditLmt(ComplexModel):
curr = mntCurrency
I have plugged these models into a simple HelloWorld Service which returns CreditLmt in response. But when I try to run my soap server, spyne complains with the following:
lxml.etree.XMLSchemaParseError: Element
'{http://www.w3.org/2001/XMLSchema}extension': The content is not
valid. Expected is (annotation?, ((group | all | choice | sequence)?,
((attribute | attributeGroup)*, anyAttribute?)))., line 16
Which is correct because spyne generates the following xsd:
<xs:complexType name="mntCurrency">
<xs:complexContent>
<xs:extension base="tns:currency">
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="code" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
<xs:sequence>
<xs:element name="test" type="xs:token" minOccurs="0" nillable="true"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
I am using XmlData because I want to have reponse such that it looks like this:
<tns:currency code="826">10.0</tns:currency>
So how do I define my models?
An example for generating this element:
<tns:currency code="826">10.0</tns:currency>
... is as follows:
from spyne import *
from spyne.util.xml import get_object_as_xml
from lxml import etree
class Currency(ComplexModel):
value = XmlData(Decimal)
code = XmlAttribute(Integer32(values=[826, 234, 555]))
class SomeObject(ComplexModel):
handle = Unicode
currency = Currency
obj = SomeObject(handle="aaaa", currency=Currency(value=D('123.45'), code=555))
elt = get_object_as_xml(obj)
print(etree.tostring(elt, pretty_print=True))
As for the error, it is coming directly from libxml. It is essentially saying that the Xml Schema standard doesn't allow lone XmlData entries in a ComplexModel subclass. If you think this is an error, you must complain to the Xml Schema working group.

Is it possible to script the creation of registered servers in SSMS 2008?

I have about 60 servers that I want to add as Registered servers for quick access. They are similarly named...is there a way to script this so I don't have to go through the wizard 60 times? Thanks!
P.S. I did check the XML file and it looks like a beast. Not sure if copying and pasting 60 times is what I want to do...
If you're comfortable in PowerShell, it can be done that way. See Registering SQL Servers in 2000 EM, 2005 SSMS, and 2008 SSMS for a starting point.
I have a script that I wrote that will generate the XML to create a .regsrvr file that can then be imported into SSMS (tested in SQL 2008). It assumes you have a source for metadata about your servers. If you have that data in a 'dba' table somewhere, it could work for you. Here's the link to my blog article. http://sqldavel.blogspot.com/2013/03/maintaining-registered-servers.html
Actually, there is a way!
It involves a few steps, but can be done via TSQL:
1) Get your list in a table.
2) Use the following Query to make the list.
Make sure you set the #FolderName to match an existing folder:
DECLARE #FolderName VARCHAR(100) = 'PROD'
DECLARE #xml_header VARCHAR(MAX) = N'<?xml version="1.0"?>
<model xmlns="http://schemas.serviceml.org/smlif/2007/02">
<identity>
<name>urn:uuid:96fe1236-abf6-4a57-b54d-e9baab394fd1</name>
<baseURI>http://documentcollection/</baseURI>
</identity>
<xs:bufferSchema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<definitions xmlns:sfc="http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08">
<document>
<docinfo>
<aliases>
<alias>/system/schema/RegisteredServers</alias>
</aliases>
<sfc:version DomainVersion="1" />
</docinfo>
<data>
<xs:schema targetNamespace="http://schemas.microsoft.com/sqlserver/RegisteredServers/2007/08" xmlns:sfc="http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08" xmlns:sml="http://schemas.serviceml.org/sml/2007/02" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="ServerGroup">
<xs:complexType>
<xs:sequence>
<xs:any namespace="http://schemas.microsoft.com/sqlserver/RegisteredServers/2007/08" processContents="skip" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="RegisteredServer">
<xs:complexType>
<xs:sequence>
<xs:any namespace="http://schemas.microsoft.com/sqlserver/RegisteredServers/2007/08" processContents="skip" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<RegisteredServers:bufferData xmlns:RegisteredServers="http://schemas.microsoft.com/sqlserver/RegisteredServers/2007/08">
<instances xmlns:sfc="http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08">
<document>
<docinfo>
<aliases>
<alias>/RegisteredServersStore/ServerGroup/DatabaseEngineServerGroup/ServerGroup/' + /*Folder_Name*/ + #FolderName + '</alias>
</aliases>
<sfc:version DomainVersion="1" />
</docinfo>
<data>
<RegisteredServers:ServerGroup xmlns:RegisteredServers="http://schemas.microsoft.com/sqlserver/RegisteredServers/2007/08" xmlns:sfc="http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08" xmlns:sml="http://schemas.serviceml.org/sml/2007/02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<RegisteredServers:RegisteredServers>
<sfc:Collection>'
, #xml_middle VARCHAR(MAX) = '</sfc:Collection>
</RegisteredServers:RegisteredServers>
<RegisteredServers:Parent>
<sfc:Reference sml:ref="true">
<sml:Uri>/RegisteredServersStore/ServerGroup/DatabaseEngineServerGroup</sml:Uri>
</sfc:Reference>
</RegisteredServers:Parent>
<RegisteredServers:Name type="string">' + #FolderName + '</RegisteredServers:Name>
<RegisteredServers:Description type="string" />
<RegisteredServers:ServerType type="ServerType">DatabaseEngine</RegisteredServers:ServerType>
</RegisteredServers:ServerGroup>
</data>
</document>'
/*Ending*/
, #xml_footer VARCHAR(MAX) = ' </instances>
</RegisteredServers:bufferData>
</xs:schema>
</data>
</document>
</definitions>
</xs:bufferSchema>
</model>'
, #xml XML
, #serverDetails VARCHAR(MAX)
, #serverReferences VARCHAR(MAX)
SELECT /*SERVER SPECIFIC*/
#serverDetails = COALESCE(#serverDetails, '') +
' <document>
<docinfo>
<aliases>
<alias>/RegisteredServersStore/ServerGroup/DatabaseEngineServerGroup/ServerGroup/' + /*Folder_Name*/ + #FolderName + '/RegisteredServer/' + SS.Server + '</alias>
</aliases>
<sfc:version DomainVersion="1" />
</docinfo>
<data>
<RegisteredServers:RegisteredServer xmlns:RegisteredServers="http://schemas.microsoft.com/sqlserver/RegisteredServers/2007/08" xmlns:sfc="http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08" xmlns:sml="http://schemas.serviceml.org/sml/2007/02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<RegisteredServers:Parent>
<sfc:Reference sml:ref="true">
<sml:Uri>/RegisteredServersStore/ServerGroup/DatabaseEngineServerGroup/ServerGroup/' + /*Folder_Name*/ #FolderName + '</sml:Uri>
</sfc:Reference>
</RegisteredServers:Parent>
<RegisteredServers:Name type="string">' + SS.Server + '</RegisteredServers:Name>
<RegisteredServers:Description type="string" />
<RegisteredServers:ServerName type="string">' + SS.Server + '</RegisteredServers:ServerName>
<RegisteredServers:UseCustomConnectionColor type="boolean">false</RegisteredServers:UseCustomConnectionColor>
<RegisteredServers:CustomConnectionColorArgb type="int">-986896</RegisteredServers:CustomConnectionColorArgb>
<RegisteredServers:ServerType type="ServerType">DatabaseEngine</RegisteredServers:ServerType>
<RegisteredServers:ConnectionStringWithEncryptedPassword type="string">data source=' + SS.Server + ';integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096</RegisteredServers:ConnectionStringWithEncryptedPassword>
<RegisteredServers:CredentialPersistenceType type="CredentialPersistenceType">None</RegisteredServers:CredentialPersistenceType>
<RegisteredServers:OtherParams type="string" />
<RegisteredServers:AuthenticationType type="int">0</RegisteredServers:AuthenticationType>
<RegisteredServers:ActiveDirectoryUserId type="string" />
<RegisteredServers:ActiveDirectoryTenant type="string" />
</RegisteredServers:RegisteredServer>
</data>
</document>
'
, #serverReferences = COALESCE(#serverReferences, '') +
' <sfc:Reference sml:ref="true">
<sml:Uri>/RegisteredServersStore/ServerGroup/DatabaseEngineServerGroup/ServerGroup/' + /*Folder_Name*/ + #FolderName + '/RegisteredServer/' + SS.Server + '</sml:Uri>
</sfc:Reference>
'
FROM /*Table with Server List*/ServerList SS
WHERE EnvironmentTypeValue IN ('PROD')
SET #xml = CAST( (#xml_header + #serverReferences + #xml_middle + #serverDetails + #xml_footer) AS xml)
SELECT #xml
3) You may need to open the XML and then copy and paste into a text file with the extension regsrvr
4) Open SSMS, go to the Registered Servers, Select the Group and First Header you wish the list to go.
5) Import and be done! :)
Note you cannot import into SQL Server 2017 from a lower version. Sadly.