Получить XPath к XElement?

Теперь вы можете использовать java 8:

Collections.addAll(list, arr);
Collections.shuffle(list);
cardsList.toArray(arr);
42
задан core 18 January 2009 в 13:46
поделиться

6 ответов

Методы расширений:

public static class XExtensions
{
    /// <summary>
    /// Get the absolute XPath to a given XElement
    /// (e.g. "/people/person[6]/name[1]/last[1]").
    /// </summary>
    public static string GetAbsoluteXPath(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        Func<XElement, string> relativeXPath = e =>
        {
            int index = e.IndexPosition();
            string name = e.Name.LocalName;

            // If the element is the root, no index is required

            return (index == -1) ? "/" + name : string.Format
            (
                "/{0}[{1}]",
                name, 
                index.ToString()
            );
        };

        var ancestors = from e in element.Ancestors()
                        select relativeXPath(e);

        return string.Concat(ancestors.Reverse().ToArray()) + 
               relativeXPath(element);
    }

    /// <summary>
    /// Get the index of the given XElement relative to its
    /// siblings with identical names. If the given element is
    /// the root, -1 is returned.
    /// </summary>
    /// <param name="element">
    /// The element to get the index of.
    /// </param>
    public static int IndexPosition(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        if (element.Parent == null)
        {
            return -1;
        }

        int i = 1; // Indexes for nodes start at 1, not 0

        foreach (var sibling in element.Parent.Elements(element.Name))
        {
            if (sibling == element)
            {
                return i;
            }

            i++;
        }

        throw new InvalidOperationException
            ("element has been removed from its parent.");
    }
}

И тест:

class Program
{
    static void Main(string[] args)
    {
        Program.Process(XDocument.Load(@"C:\test.xml").Root);
        Console.Read();
    }

    static void Process(XElement element)
    {
        if (!element.HasElements)
        {
            Console.WriteLine(element.GetAbsoluteXPath());
        }
        else
        {
            foreach (XElement child in element.Elements())
            {
                Process(child);
            }
        }
    }
}

И демонстрационный вывод:

/tests/test[1]/date[1]
/tests/test[1]/time[1]/start[1]
/tests/test[1]/time[1]/end[1]
/tests/test[1]/facility[1]/name[1]
/tests/test[1]/facility[1]/website[1]
/tests/test[1]/facility[1]/street[1]
/tests/test[1]/facility[1]/state[1]
/tests/test[1]/facility[1]/city[1]
/tests/test[1]/facility[1]/zip[1]
/tests/test[1]/facility[1]/phone[1]
/tests/test[1]/info[1]
/tests/test[2]/date[1]
/tests/test[2]/time[1]/start[1]
/tests/test[2]/time[1]/end[1]
/tests/test[2]/facility[1]/name[1]
/tests/test[2]/facility[1]/website[1]
/tests/test[2]/facility[1]/street[1]
/tests/test[2]/facility[1]/state[1]
/tests/test[2]/facility[1]/city[1]
/tests/test[2]/facility[1]/zip[1]
/tests/test[2]/facility[1]/phone[1]
/tests/test[2]/info[1]

, Который должен уладить это. Нет?

42
ответ дан 9 revs 23 September 2019 в 14:04
поделиться

Может быть несколько xpaths, которые приводят к тому же элементу, таким образом находя самый простой xpath, который приводит к узлу, не тривиально.

Однако довольно легко найти xpath к узлу. Просто повысьте дерева узла, пока Вы не читаете корневой узел и комбинируете имена узла, и у Вас есть допустимый xpath.

0
ответ дан Rune Grimstad 23 September 2019 в 14:04
поделиться

При поиске чего-то исходно обеспеченного.NET, ответ нет. Необходимо было бы записать собственный дополнительный метод, чтобы сделать это.

0
ответ дан Scott Dorman 23 September 2019 в 14:04
поделиться

"Полным xpath" я предполагаю, что Вы имеете в виду простую цепочку тегов начиная с количества xpaths, который мог потенциально соответствовать любому элементу, мог быть очень большие.

проблема здесь состоит в том, что очень трудно, если не специфически невозможный создать кого-либо данного xpath, который обратимо проследит до того же элемента - который является условием?

, Если "нет" тогда, возможно, Вы могли бы создать запрос рекурсивно цикличным выполнением со ссылкой на элементы тока parentNode. Если "да", то Вы собираетесь быть рассмотрением расширения этого перекрестными ссылками для индексного положения в одноуровневых наборах, referecing подобные ИДЕНТИФИКАТОРУ атрибуты, если они существуют, и это будет очень иждивенцем на Вашем XSD, если общее решение возможно.

0
ответ дан annakata 23 September 2019 в 14:04
поделиться

Это - на самом деле дубликат этого вопроса. В то время как это не отмечено как ответ, метод в моем ответе на тот вопрос является единственным способом однозначной формулировки XPath к узлу в рамках XML-документа, который будет всегда работать при всех обстоятельствах. (Это также работает на все типы узлов, не просто элементы.)

Как Вы видите, XPath это, продукты ужасны и абстрактны. но это обращается к опасениям, что много отвечающих сторон повысили здесь. Большинство предложений, сделанных здесь, производит XPath, который при использовании для поиска оригинала документа произведет ряд одного или нескольких узлов, который включает целевой узел. Это - это "или больше", это - проблема. Например, если у меня есть представление XML DataSet, наивного XPath к элементу определенного DataRow, /DataSet1/DataTable1, также возвращает элементы всех других DataRows в DataTable. Вы не можете снять неоднозначность этого, не зная что-то о том, как XML является forumlated (как, есть ли элемент первичного ключа?).

Но /node()[1]/node()[4]/node()[11], существует только один узел, который это будет когда-либо возвращать, несмотря ни на что.

4
ответ дан Community 26 November 2019 в 23:52
поделиться

Я обновил код Криса, чтобы учесть префиксы пространства имен. Изменяется только метод GetAbsoluteXPath.

public static class XExtensions
{
    /// <summary>
    /// Get the absolute XPath to a given XElement, including the namespace.
    /// (e.g. "/a:people/b:person[6]/c:name[1]/d:last[1]").
    /// </summary>
    public static string GetAbsoluteXPath(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        Func<XElement, string> relativeXPath = e =>
        {
            int index = e.IndexPosition();

            var currentNamespace = e.Name.Namespace;

            string name;
            if (currentNamespace == null)
            {
                name = e.Name.LocalName;
            }
            else
            {
                string namespacePrefix = e.GetPrefixOfNamespace(currentNamespace);
                name = namespacePrefix + ":" + e.Name.LocalName;
            }

            // If the element is the root, no index is required
            return (index == -1) ? "/" + name : string.Format
            (
                "/{0}[{1}]",
                name,
                index.ToString()
            );
        };

        var ancestors = from e in element.Ancestors()
                        select relativeXPath(e);

        return string.Concat(ancestors.Reverse().ToArray()) +
               relativeXPath(element);
    }

    /// <summary>
    /// Get the index of the given XElement relative to its
    /// siblings with identical names. If the given element is
    /// the root, -1 is returned.
    /// </summary>
    /// <param name="element">
    /// The element to get the index of.
    /// </param>
    public static int IndexPosition(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        if (element.Parent == null)
        {
            return -1;
        }

        int i = 1; // Indexes for nodes start at 1, not 0

        foreach (var sibling in element.Parent.Elements(element.Name))
        {
            if (sibling == element)
            {
                return i;
            }

            i++;
        }

        throw new InvalidOperationException
            ("element has been removed from its parent.");
    }
}
11
ответ дан 26 November 2019 в 23:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: