네임 스페이스를 모르고 LINQ를 사용하여 XDocument 검색


81

네임 스페이스를 모르고 XDocument를 검색하는 방법이 있습니까? 모든 SOAP 요청을 기록하고 민감한 데이터를 암호화하는 프로세스가 있습니다. 이름을 기준으로 요소를 찾고 싶습니다. 이름이 CreditCard 인 모든 요소를 ​​제공합니다. 나는 네임 스페이스가 무엇인지 상관하지 않습니다.

내 문제는 LINQ에 있고 xml 네임 스페이스가 필요한 것 같습니다.

XML에서 값을 검색하는 다른 프로세스가 있지만 이러한 다른 프로세스의 네임 스페이스를 알고 있습니다.

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
XNamespace xNamespace = "http://CompanyName.AppName.Service.Contracts";

var elements = xDocument.Root
                        .DescendantsAndSelf()
                        .Elements()
                        .Where(d => d.Name == xNamespace + "CreditCardNumber");

다음과 같이 네임 스페이스에 대해 몰라도 xml을 검색 할 수있는 기능을 갖고 싶습니다.

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
var elements = xDocument.Root
                        .DescendantsAndSelf()
                        .Elements()
                        .Where(d => d.Name == "CreditCardNumber")

컴파일 타임에 네임 스페이스를 미리 알지 못하기 때문에 작동하지 않습니다.

어떻게 할 수 있습니까?

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Request xmlns="http://CompanyName.AppName.Service.ContractA">
        <Person>
            <CreditCardNumber>83838</CreditCardNumber>
            <FirstName>Tom</FirstName>
            <LastName>Jackson</LastName>
        </Person>
        <Person>
            <CreditCardNumber>789875</CreditCardNumber>
            <FirstName>Chris</FirstName>
            <LastName>Smith</LastName>
        </Person>
        ...

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Request xmlns="http://CompanyName.AppName.Service.ContractsB">
        <Transaction>
            <CreditCardNumber>83838</CreditCardNumber>
            <TransactionID>64588</FirstName>
        </Transaction>      
        ...

다른 질문에서이 답변을 확인하십시오 : stackoverflow.com/questions/934486/…
— MonkeyWrench

답변:


90

Adam이 주석에서 정확하게 설명했듯이 XName은 문자열로 변환 할 수 있지만 해당 문자열에는 네임 스페이스가 있으면 네임 스페이스가 필요합니다. 이것이 바로 .Name과 문자열의 비교가 실패하는 이유이거나 "Person"을 매개 변수로 XLinq 메서드에 전달하여 이름을 필터링 할 수없는 이유입니다.
XName은 접두사 (네임 스페이스)와 LocalName으로 구성됩니다. 로컬 이름은 네임 스페이스를 무시하는 경우 쿼리하려는 이름입니다.
아담 감사합니다 :)

노드의 이름을 .Descendants () 메서드의 매개 변수로 넣을 수는 없지만 다음과 같이 쿼리 할 수 ​​있습니다.

var doc= XElement.Parse(
@"<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
<s:Body xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
  <Request xmlns=""http://CompanyName.AppName.Service.ContractA"">
    <Person>
        <CreditCardNumber>83838</CreditCardNumber>
        <FirstName>Tom</FirstName>
        <LastName>Jackson</LastName>
    </Person>
    <Person>
        <CreditCardNumber>789875</CreditCardNumber>
        <FirstName>Chris</FirstName>
        <LastName>Smith</LastName>
    </Person>
   </Request>
   </s:Body>
</s:Envelope>");

편집 : 내 테스트에서 잘못된 사본 / 과거 :)

var persons = from p in doc.Descendants()
              where p.Name.LocalName == "Person"
              select p;

foreach (var p in persons)
{
    Console.WriteLine(p);
}

그것은 나를 위해 작동합니다 ...


5
당신의 대답이 왜 그런지에 대한 설명을 넣는 데 도움 이 될 수 있습니다. 이름은 XName이고 XName은 문자열로 변환 가능하므로 .Name과 문자열의 비교는 질문 질문자의 쿼리로 실패합니다. XName은 접두사와 로컬 이름으로 구성되며 로컬 이름은 네임 스페이스를 무시하는 경우 쿼리하려는 이름입니다.
— Adam Sills

그것은 내가 somerockstar 대답에 넣은 주석에있었습니다. 명확성을 위해 추가 할 수 있습니다. 맞습니다
— Stéphane

빠른 도움에 감사드립니다. 바라건대 이것은 다른 사람을 도울 것입니다.
— Mike Barlow-BarDev 2010

희망은 그래서, 저도 같은 문제에 처음으로 XLinq를 사용하여 :) 붙어있어
— 스테판

1
@ MikeBarlow-BarDev 그것은했다 ;-)
— Simon_Weaver

88

루트 요소에서 네임 스페이스를 가져올 수 있습니다.

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
var ns = xDocument.Root.Name.Namespace;

이제 더하기 연산자를 사용하여 원하는 모든 요소를 ​​쉽게 얻을 수 있습니다.

root.Elements(ns + "CreditCardNumber")

여전히 대부분의 LINQ작업 을 사용할 수 있기 때문에 더 나은 대답처럼 보입니다 .
— Ehtesh Choudhury 2016 년

6
이 대답은 루트 문서와 다른 네임 스페이스에 요소가없는 경우에만 허용됩니다. 예, 루트 문서에 요청하면 네임 스페이스를 쉽게 알 수 있지만 요소 자체가 어떤 네임 스페이스에 있는지에 관계없이 주어진 이름의 요소를 요청하는 것이 더 까다 롭습니다. 그래서 대답이 XElement를 사용한다고 생각하는 것입니다. Name.LocalName (일반적으로 linq를 통해)이 더 일반화되었습니다.
— Caleb Holt

이 대답은 충분히 일반적이지 않습니다.
— ceztko

14

내가 찾던 것을 찾은 것 같아요. 다음 코드에서 내가 평가를 수행하는 것을 볼 수 있습니다 Element.Name.LocalName == "CreditCardNumber". 이것은 내 테스트에서 작동하는 것처럼 보였습니다. 모범 사례인지 확실하지 않지만 사용하겠습니다.

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
var elements = xDocument.Root.DescendantsAndSelf().Elements().Where(d => d.Name.LocalName == "CreditCardNumber");

이제 값을 암호화 할 수있는 요소가 있습니다.

누구든지 더 나은 솔루션이 있으면 제공하십시오. 감사.


네임 스페이스를 모르거나 신경 쓰지 않는 경우 완벽한 솔루션입니다. 감사합니다!
— SeriousM 2018-04-24

2

XML 문서가 항상 동일한 노드 ( Request주어진 두 예제의 노드)에서 네임 스페이스를 정의하는 경우 쿼리를 만들고 결과에 어떤 네임 스페이스가 있는지 확인하여 네임 스페이스를 결정할 수 있습니다.

XDocument xDoc = XDocument.Load("filename.xml");
//Initial query to get namespace:
var reqNodes = from el in xDoc.Root.Descendants()
               where el.Name.LocalName == "Request"
               select el;
foreach(var reqNode in reqNodes)
{
    XNamespace xns = reqNode.Name.Namespace;
    //Queries making use of namespace:
    var person = from el in reqNode.Elements(xns + "Person")
                 select el;
}

2

삭제 된 확장 방법에 대한 몇 가지 답변이 있습니다. 이유가 확실하지 않습니다. 내 필요에 맞는 내 버전이 있습니다.

public static class XElementExtensions
{
    public static XElement ElementByLocalName(this XElement element, string localName)
    {
        return element.Descendants().FirstOrDefault(e => e.Name.LocalName == localName && !e.IsEmpty);
    }
}

다음 IsEmpty으로 노드를 필터링하는 것입니다.x:nil="true"

추가적인 미묘함이있을 수 있으므로주의해서 사용하십시오.


아름다운! 감사합니다 Simon. 나는 이것이 유일한 정답 이라고 거의 말할 것입니다 ....이 작업을 한 번 수행하면 100 번을 수행하고 다른 모든 답변은 el.ElementByLocalName ( "foo")에 비해 상당히 서투른 것입니다. .
— Tim Cooper

-7

Descendents 방법을 사용하십시오.

XDocument doc = XDocument.Load(filename);
String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber")
                        select creditCardNode.Value).ToArray<string>();

3
하위 매개 변수가 XName을 요청하기 때문에 작동하지 않으며 여기서 XName 앞에 네임 스페이스가 붙습니다.
— Stéphane
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.