web-dev-qa-db-ja.com

名前空間を知らずにLINQを使用してXDocumentを検索する

名前空間を知らずにXDocumentを検索する方法はありますか?すべてのSOAP=リクエストをログに記録し、機密データを暗号化するプロセスがあります。名前に基づいて任意の要素を検索したいです。名前空間が何であるかに注意してください。

私の問題は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>      
        ...
73

Adamがコメントで詳しく述べているように、XNameは文字列に変換できますが、その文字列には名前空間が必要です。そのため、.Nameと文字列の比較が失敗するか、名前でフィルター処理するためにXLinqメソッドにパラメーターとして "Person"を渡すことができません。
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);
}

それは私のために働く...

83
Stéphane

ルート要素から名前空間を取得できます。

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

これで、プラス演算子を使用して、必要なすべての要素を簡単に取得できます。

root.Elements(ns + "CreditCardNumber")
81
Sascha

私が探していたものを見つけたと思う。次のコードで評価を行うことができますElement.Name.LocalName == "CreditCardNumber"。これは私のテストではうまくいくように見えました。ベストプラクティスであるかどうかはわかりませんが、使用するつもりです。

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

これで、値を暗号化できる要素ができました。

誰かがより良い解決策を持っているなら、それを提供してください。ありがとう。

12

XMLドキュメントが常に同じノード(指定された2つの例の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
Clivest

削除された拡張メソッドにはいくつかの回答があります。理由はわかりません。これが私のニーズに合った私のバージョンです。

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"でノードを除外することです

追加の微妙な点があるかもしれないので、注意して使用してください。

0
Simon_Weaver