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

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



답변

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

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


답변

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

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

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

root.Elements(ns + "CreditCardNumber")


답변

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

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

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

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


답변

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;
}


답변

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

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"

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


답변

Descendents 방법을 사용하십시오.

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


답변