문제

문서 안에 깊은 Xelement가 있습니다. xlement (및 xdocument?)를 감안할 때, 전체를 얻을 수있는 확장 방법이 있습니까 (예 : /root/item/element/child) xpath?

예 : myxelement.getxpath ()?

편집 : 좋아, 내가 매우 중요한 것을 간과 한 것 같습니다. 멍청이! 요소의 색인을 고려해야합니다. 제안 된 수정 솔루션에 대한 마지막 답변을 참조하십시오.

도움이 되었습니까?

해결책

확장 방법 :

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]

이것을 해결해야합니다. 아니?

다른 팁

Chris의 코드를 업데이트하여 네임 스페이스 접두사를 고려했습니다. 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.");
    }
}

이 수업에 대한 최신 수정을 공유하겠습니다. 기본 IT 요소에 형제가없고 Local-Name () 연산자가있는 네임 스페이스가 포함 된 경우 INDEX를 제외합니다.

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 (String.IsNullOrEmpty(currentNamespace.ToString()))
            {
                name = e.Name.LocalName;
            }
            else
            {
                name = "*[local-name()='" + e.Name.LocalName + "']";
                //string namespacePrefix = e.GetPrefixOfNamespace(currentNamespace);
                //name = namespacePrefix + ":" + e.Name.LocalName;
            }

            // If the element is the root or has no sibling elements, no index is required
            return ((index == -1) || (index == -2)) ? "/" + 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 or -2 if element has no sibling elements.
    /// </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)
        {
            // Element is root
            return -1;
        }

        if (element.Parent.Elements(element.Name).Count() == 1)
        {
            // Element has no sibling elements
            return -2;
        }

        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.");
    }
}

이것은 실제로의 복제입니다 이것 의문. 답으로 표시되지는 않지만 방법은 내 대답 그 질문은 모든 상황에서 항상 작동하는 XML 문서 내의 노드로 XPath를 모호하게 공식화하는 유일한 방법입니다. (요소뿐만 아니라 모든 노드 유형에도 작동합니다.)

보시다시피, 그것이 생산하는 xpath는 추악하고 추상적입니다. 그러나 그것은 많은 답변가들이 여기에서 제기 한 우려를 다룹니다. 여기에 제조 된 대부분의 제안은 원본 문서를 검색하는 데 사용될 때 대상 노드를 포함하는 하나 이상의 노드 세트를 생성하는 XPath를 생성합니다. "또는 더 많은"이 문제입니다. 예를 들어, 데이터 세트의 XML 표현이있는 경우 특정 Datarow의 요소에 대한 순진한 xpath는 /DataSet1/DataTable1, 또한 데이터 가능에 다른 모든 데이터 타로의 요소를 반환합니다. XML이 어떻게 포럼으로 표시되는지에 대해 알지 못하고 (1 차 키 요소가 있습니까?)를 명확하게 할 수 없습니다.

하지만 /node()[1]/node()[4]/node()[11], 무엇이든지 반환 할 노드는 단 하나뿐입니다.

a의 일부로 다른 프로젝트 요소에 대한 간단한 xpath를 생성하기 위해 확장 방법을 개발했습니다. 선택한 답변과 유사하지만 Xattribute, Xtext, XCData 및 Xcomment를 Xlement 외에 지원합니다. 사용할 수 있습니다 코드 Nuget, 프로젝트 페이지는 여기에 : xmlspecificationcompare.codeplex.com

.NET가 기본적으로 제공 한 것을 찾고 있다면 답은 아니오입니다. 이를 위해 자신의 확장 방법을 작성해야합니다.

동일한 요소로 이어지는 몇 가지 XPath가있을 수 있으므로 노드로 이어지는 가장 간단한 XPATH를 찾는 것은 사소하지 않습니다.

즉, 노드에 대한 xpath를 찾는 것은 매우 쉽습니다. 루트 노드를 읽고 노드 이름을 결합 할 때까지 노드 트리를 강화하면 유효한 xpath가 있습니다.

"full xpath"에 의해 나는 당신이 모든 요소와 일치 할 수있는 xpath의 수가 될 수 있기 때문에 간단한 태그 체인을 의미한다고 생각합니다 매우 크기가 큰.

여기서 문제는 동일한 요소로 가역적으로 추적 할 주어진 XPath를 구축하는 것이 불가능하지 않으면 매우 어렵다는 것입니다. 조건입니까?

"아니오"라면 아마도 현재 요소 ParentNode를 참조하여 재귀 적으로 반복하여 쿼리를 만들 수 있습니다. "예"라면, 당신은 형제 세트 내에서 인덱스 위치에 대한 크로스 참조를 통해이를 확장하고, ID와 유사한 속성이 존재하는 경우, 이는 일반적인 솔루션 인 경우 XSD에 매우 의존 할 것입니다. 가능합니다.

Microsoft는 .NET Framework 3.5 이후에이를 수행하기위한 확장 방법을 제공했습니다.

http://msdn.microsoft.com/en-us/library/bb156083(v=vs.100).aspx

사용을 추가하십시오 System.Xml.XPath 다음 방법을 호출합니다.

  • XPathSelectElement: 단일 요소를 선택하십시오
  • XPathSelectElements: 요소를 선택하고 an IEnumerable<XElement>
  • XPathEvaluate: 노드 (요소뿐만 아니라 텍스트, 주석 등)를 선택하고 IEnumerable<object>
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top