试图列出已声明的 XML-namespaces
Trying to list declared XML-namespaces
我们有许多应用程序通过提供 XML 数据来访问我们的 API。在某个时候,我们决定对传入的 NULL
值使用 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"
,以便能够以统一的方式区分空值和 NULL
值。
因为向 xsi:nil
的过渡仍在进行中,我希望能够判断 xsi
-命名空间是否被声明为调用应用程序是否会使用的指示器xsi:nil="true"
对应 NULL
个值。
我试过了
DECLARE @SomeXML xml = N'<ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><blubb><blah xsi:nil="true"/></blubb></ROOT>';
SELECT @SomeXML.exist('declare namespace xsi="http://www.w3.org/2001/XMLSchema-instance"; //xsi:*, //@xsi:*');
但这只有在 XML 文档中实际引用了命名空间时才有效。像 //@xmlns:*
这样的查询会导致错误
Msg 2229, Level 16, State 1, Line 6
XQuery [query()]: The name "xmlns" does not denote a namespace.
而对 //@*:xsi
的查询只是 returns 什么都没有。
有什么方法可以确定 SQL Server 2016 中声明的 XML-命名空间?
我不确定是否有比转换为 nvarchar
并在 URI 上执行 CHARINDEX
更好的方法。我认为你不太可能得到误报,但感觉不对。
DECLARE @SomeXML xml = N'<ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><blubb><blah xsi:nil="true"/></blubb></ROOT>'
select CHARINDEX(N'"http://www.w3.org/2001/XMLSchema-instance"',CONVERT(nvarchar(max), @SomeXML))
(我们只在 URI 上搜索,这样即使在文档中使用了不同的前缀我们也能找到它)
问题是大多数 XML 工具都假定每个 "context"(例如 XML 文档、XPath 表达式等)都引入了与其相关的名称空间,因此不需要一种机制来探索其他 "contexts".
中的命名空间声明
我从this thread, that there is a rather old way to enumerate namespaces utilizing OPENXML
那里学到了:
DECLARE @SomeXML xml = N'<ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2001/XMLSchema-instance"><blubb xmlns:xsiu="http://www.w3.org/2001/XMLSchema-instance"><blah xsi:nil="true"/></blubb></ROOT>';
DECLARE @hDoc int;
EXEC sys.sp_xml_preparedocument
@hDoc OUTPUT,
@SomeXML;
IF EXISTS ( SELECT namespace = NULLIF(XmlnsAttribute.localname, 'xmlns'),
namespace_uri = XmlnsValue.text
FROM OPENXML( @hDoc, '//*' ) XmlnsAttribute
INNER JOIN OPENXML( @hDoc, '//*' ) XmlnsValue ON XmlnsValue.parentid = XmlnsAttribute.id
WHERE XmlnsAttribute.prefix = 'xmlns'
AND XmlnsValue.nodetype = 3 /*text*/
AND CAST(XmlnsValue.text AS nvarchar(MAX)) = N'http://www.w3.org/2001/XMLSchema-instance' )
PRINT 'Has http://www.w3.org/2001/XMLSchema-instance namespace';
ELSE
PRINT 'Does not have http://www.w3.org/2001/XMLSchema-instance namespace';
EXEC sys.sp_xml_removedocument
@hDoc;
我不确定这有多优雅,因为 text
列是 ntext
类型,并且对 sys.sp_xml_preparedocument
和 sys.sp_xml_removedocument
的调用意味着,您需要当您将其包含在其他查询中时,请多加注意。可能最糟糕的缺点是,您需要执行此 RBAR。
这仍然是一个没有将xml
转换为varchar
的解决方案,所以应该很难被欺骗。
列出所有命名空间:
DECLARE @SomeXML xml = N'<ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2001/XMLSchema-instance"><blubb xmlns:xsiu="http://www.w3.org/2001/XMLSchema-instance"><blah xsi:nil="true"/></blubb></ROOT>';
DECLARE @hDoc int;
EXEC sys.sp_xml_preparedocument
@hDoc OUTPUT,
@SomeXML;
-- All registered namespaces
WITH XmlNodes
AS (SELECT OX.id,
OX.parentid,
OX.nodetype,
OX.localname,
OX.prefix,
OX.text
FROM OPENXML( @hDoc, '//*' ) OX)
SELECT namespace = NULLIF(XmlnsAttribute.localname, 'xmlns'),
namespace_uri = XmlnsValue.text
FROM XmlNodes XmlnsAttribute
INNER JOIN XmlNodes XmlnsValue ON XmlnsValue.parentid = XmlnsAttribute.id
WHERE XmlnsAttribute.prefix = 'xmlns'
AND XmlnsValue.nodetype = 3
/*text*/;
-- All registered namespaces with scope
WITH XmlNodes
AS (SELECT OX.id,
OX.parentid,
OX.nodetype,
OX.localname,
OX.prefix,
OX.text
FROM OPENXML( @hDoc, '//*' ) OX),
XmlNodesWithPath
AS (SELECT XN.id,
path = CAST(N'/' + ISNULL(XN.prefix + N':', N'') + XN.localname AS nvarchar(MAX))
FROM XmlNodes XN
WHERE XN.parentid IS NULL
UNION ALL
SELECT XN.id,
path = XNWP.path + N'/' + ISNULL(XN.prefix + N':', N'') + XN.localname
FROM XmlNodesWithPath XNWP
INNER JOIN XmlNodes XN ON XN.parentid = XNWP.id
AND XN.nodetype = 1)
SELECT scope = Scope.path,
namespace = NULLIF(XmlnsAttribute.localname, 'xmlns'),
namespace_uri = XmlnsValue.text
FROM XmlNodesWithPath Scope
INNER JOIN XmlNodes XmlnsAttribute ON XmlnsAttribute.parentid = Scope.id
INNER JOIN XmlNodes XmlnsValue ON XmlnsValue.parentid = XmlnsAttribute.id
WHERE XmlnsAttribute.prefix = 'xmlns'
AND XmlnsValue.nodetype = 3
/*text*/;
EXEC sys.sp_xml_removedocument
@hDoc;
我们有许多应用程序通过提供 XML 数据来访问我们的 API。在某个时候,我们决定对传入的 NULL
值使用 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"
,以便能够以统一的方式区分空值和 NULL
值。
因为向 xsi:nil
的过渡仍在进行中,我希望能够判断 xsi
-命名空间是否被声明为调用应用程序是否会使用的指示器xsi:nil="true"
对应 NULL
个值。
我试过了
DECLARE @SomeXML xml = N'<ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><blubb><blah xsi:nil="true"/></blubb></ROOT>';
SELECT @SomeXML.exist('declare namespace xsi="http://www.w3.org/2001/XMLSchema-instance"; //xsi:*, //@xsi:*');
但这只有在 XML 文档中实际引用了命名空间时才有效。像 //@xmlns:*
这样的查询会导致错误
Msg 2229, Level 16, State 1, Line 6
XQuery [query()]: The name "xmlns" does not denote a namespace.
而对 //@*:xsi
的查询只是 returns 什么都没有。
有什么方法可以确定 SQL Server 2016 中声明的 XML-命名空间?
我不确定是否有比转换为 nvarchar
并在 URI 上执行 CHARINDEX
更好的方法。我认为你不太可能得到误报,但感觉不对。
DECLARE @SomeXML xml = N'<ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><blubb><blah xsi:nil="true"/></blubb></ROOT>'
select CHARINDEX(N'"http://www.w3.org/2001/XMLSchema-instance"',CONVERT(nvarchar(max), @SomeXML))
(我们只在 URI 上搜索,这样即使在文档中使用了不同的前缀我们也能找到它)
问题是大多数 XML 工具都假定每个 "context"(例如 XML 文档、XPath 表达式等)都引入了与其相关的名称空间,因此不需要一种机制来探索其他 "contexts".
中的命名空间声明我从this thread, that there is a rather old way to enumerate namespaces utilizing OPENXML
那里学到了:
DECLARE @SomeXML xml = N'<ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2001/XMLSchema-instance"><blubb xmlns:xsiu="http://www.w3.org/2001/XMLSchema-instance"><blah xsi:nil="true"/></blubb></ROOT>';
DECLARE @hDoc int;
EXEC sys.sp_xml_preparedocument
@hDoc OUTPUT,
@SomeXML;
IF EXISTS ( SELECT namespace = NULLIF(XmlnsAttribute.localname, 'xmlns'),
namespace_uri = XmlnsValue.text
FROM OPENXML( @hDoc, '//*' ) XmlnsAttribute
INNER JOIN OPENXML( @hDoc, '//*' ) XmlnsValue ON XmlnsValue.parentid = XmlnsAttribute.id
WHERE XmlnsAttribute.prefix = 'xmlns'
AND XmlnsValue.nodetype = 3 /*text*/
AND CAST(XmlnsValue.text AS nvarchar(MAX)) = N'http://www.w3.org/2001/XMLSchema-instance' )
PRINT 'Has http://www.w3.org/2001/XMLSchema-instance namespace';
ELSE
PRINT 'Does not have http://www.w3.org/2001/XMLSchema-instance namespace';
EXEC sys.sp_xml_removedocument
@hDoc;
我不确定这有多优雅,因为 text
列是 ntext
类型,并且对 sys.sp_xml_preparedocument
和 sys.sp_xml_removedocument
的调用意味着,您需要当您将其包含在其他查询中时,请多加注意。可能最糟糕的缺点是,您需要执行此 RBAR。
这仍然是一个没有将xml
转换为varchar
的解决方案,所以应该很难被欺骗。
列出所有命名空间:
DECLARE @SomeXML xml = N'<ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2001/XMLSchema-instance"><blubb xmlns:xsiu="http://www.w3.org/2001/XMLSchema-instance"><blah xsi:nil="true"/></blubb></ROOT>';
DECLARE @hDoc int;
EXEC sys.sp_xml_preparedocument
@hDoc OUTPUT,
@SomeXML;
-- All registered namespaces
WITH XmlNodes
AS (SELECT OX.id,
OX.parentid,
OX.nodetype,
OX.localname,
OX.prefix,
OX.text
FROM OPENXML( @hDoc, '//*' ) OX)
SELECT namespace = NULLIF(XmlnsAttribute.localname, 'xmlns'),
namespace_uri = XmlnsValue.text
FROM XmlNodes XmlnsAttribute
INNER JOIN XmlNodes XmlnsValue ON XmlnsValue.parentid = XmlnsAttribute.id
WHERE XmlnsAttribute.prefix = 'xmlns'
AND XmlnsValue.nodetype = 3
/*text*/;
-- All registered namespaces with scope
WITH XmlNodes
AS (SELECT OX.id,
OX.parentid,
OX.nodetype,
OX.localname,
OX.prefix,
OX.text
FROM OPENXML( @hDoc, '//*' ) OX),
XmlNodesWithPath
AS (SELECT XN.id,
path = CAST(N'/' + ISNULL(XN.prefix + N':', N'') + XN.localname AS nvarchar(MAX))
FROM XmlNodes XN
WHERE XN.parentid IS NULL
UNION ALL
SELECT XN.id,
path = XNWP.path + N'/' + ISNULL(XN.prefix + N':', N'') + XN.localname
FROM XmlNodesWithPath XNWP
INNER JOIN XmlNodes XN ON XN.parentid = XNWP.id
AND XN.nodetype = 1)
SELECT scope = Scope.path,
namespace = NULLIF(XmlnsAttribute.localname, 'xmlns'),
namespace_uri = XmlnsValue.text
FROM XmlNodesWithPath Scope
INNER JOIN XmlNodes XmlnsAttribute ON XmlnsAttribute.parentid = Scope.id
INNER JOIN XmlNodes XmlnsValue ON XmlnsValue.parentid = XmlnsAttribute.id
WHERE XmlnsAttribute.prefix = 'xmlns'
AND XmlnsValue.nodetype = 3
/*text*/;
EXEC sys.sp_xml_removedocument
@hDoc;