Ответ 1
Этот вопрос уже ответил на комментарии, так что это всего лишь компиляция с небольшим количеством объяснений.
Для экспериментов мы будем использовать этот XML:
DECLARE @XML XML =
'<root root_attr="0">
<leaf leaf_attr="1">one</leaf>
<brunch brunch_attr="2">
<leaf leaf_attr="3">three</leaf>
</brunch>
</root>';
И нам нужно извлечь список атрибутов корневого элемента: root_attr="0"
.
Для справки XPath мы ссылаемся на Руководство по синтаксису MSDN XPath
Итак, "/" означает "child" или "root node", если он появляется в начале шаблона, "@" означает "атрибут", "*" означает "any" и ".". означает "текущий контекст". Конечно, это должно дать нам все корневые атрибуты:
SELECT
CAST(attribute.name.query('local-name(.)') AS VARCHAR(MAX)) As [Name],
attribute.name.value('.','NVARCHAR(MAX)') As [Value]
FROM @XML.nodes('/@*') attribute(name);
Вместо этого возникает ошибка: Узлы атрибутов верхнего уровня не поддерживаются.
В XML есть два типа узлов: < element > Элемент Value </element> и < элемент атрибут= "Значение атрибута" / > . Таким образом, /@*
XPath интерпретируется как любой атрибут для корня XML, а не корневого элемента. Фактически это можно проиллюстрировать следующим образом:
SELECT
CAST(attribute.name.query('local-name(.)') AS VARCHAR(MAX)) As [Name],
attribute.name.value('.','NVARCHAR(MAX)') As [Value]
FROM @XML.nodes('/') attribute(name);
Возврат:
Name Value
---- --------
onethree
Что представляет собой анонимный node, представляющий весь XML-документ. '.'
XPath даст тот же результат.
Итак, нам нужно указать любой элемент в корне XML-документа. Синтаксис для этого должен быть "//" (дочерний элемент анонимного корня node= root), если это выражение не означает "рекурсивный спуск" (все дети). Действительно
SELECT
CAST(attribute.name.query('local-name(.)') AS VARCHAR(MAX)) As [Name],
attribute.name.value('.','NVARCHAR(MAX)') As [Value]
FROM @XML.nodes('//@*') attribute(name);
Возвращает полный список атрибутов всех элементов:
Name Value
----------- --------
root_attr 0
leaf_attr 1
brunch_attr 2
leaf_attr 3
Хорошо, теперь нам нужен способ сказать в XPath "root" "element" вместо "rootelement", который, по-видимому, является зарезервированным словом. Один из способов - сжать "любой", другой - указать, что он должен быть "node()", если только из-за причины мы не знаем фактическое имя корневого элемента.
Для данного XML эти три равны:
SELECT
CAST(attribute.name.query('local-name(.)') AS VARCHAR(MAX)) As [Name],
attribute.name.value('.','NVARCHAR(MAX)') As [Value]
FROM @XML.nodes('/*/@*') attribute(name);
SELECT
CAST(attribute.name.query('local-name(.)') AS VARCHAR(MAX)) As [Name],
attribute.name.value('.','NVARCHAR(MAX)') As [Value]
FROM @XML.nodes('/node()/@*') attribute(name);
SELECT
CAST(attribute.name.query('local-name(.)') AS VARCHAR(MAX)) As [Name],
attribute.name.value('.','NVARCHAR(MAX)') As [Value]
FROM @XML.nodes('/root/@*') attribute(name);
Возврат:
Name Value
--------- --------
root_attr 0
Вот мы. Немного тавтологии XPath для работы с зарезервированным словом "//".