네임 스페이스를 모르고 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.