Необходимо получить пар атрибут имени атрибута только для корневого элемента, в запросе T-SQL

Я создал табличную функцию, которая возвращает список пар атрибутов имени-атрибута, когда кормится фрагмент XML, основанный на отличном ответе Бена Дэвиса здесь, Он работает, но возвращает список всех пар атрибутов имени атрибута во всем фрагменте, когда я хотел бы ограничить его только на корневом элементе. Как я могу это сделать? Спасибо, от новичка XQuery.

INSERT INTO @attributeList
SELECT DISTINCT
    CAST(attribute.name.query('local-name(.)') AS VARCHAR(100)),
    attribute.name.value('.','NVARCHAR(MAX)')
FROM @xml.nodes('//@*') attribute(name)

ETA: Как выясняется после некоторых экспериментов, работает селектор node()/@* '. Спасибо тем, кто помог.

Я использую это в простом приложении переноса данных или в загрузочном файле Sitecore на передней панели. Я написал утилиту, которая может принимать объекты POCO в .NET и получать их в Sitecore, но теперь я создал базу данных миграции и регистрации. Исходные объекты хранятся в одном месте как XML. Еще раз спасибо.

Ответ 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 для работы с зарезервированным словом "//".