次の方法で共有


XML ツリーの構造を変換する方法 (LINQ to XML)

XML ドキュメントの "構造" とは、XML ドキュメントの要素名、属性名、およびその階層の特性を表すものです。

XML ドキュメントの構造を変更しなければならない場合があります。 たとえば、既存の XML ドキュメントを、異なる要素名と属性名を必要とする別のシステムに送信するとします。 ドキュメント全体を調べ、必要に応じて要素の削除や要素名の変更を行うことも可能ですが、関数型構築を使用する方が、読みやすく保守しやすいコードを生成できます。 関数型構築の詳細については、「関数型構築」を参照してください。

この記事の最初の例では、XML ドキュメントの構成が変更されます。 ここでは、複合要素をツリー内で移動します。

2 番目の例では、構造がソース ドキュメントの構造とは異なる XML ドキュメントが作成されます。 一部の要素が省略され、名前が変更され、要素名の大文字と小文字の使い分けが変更されます。

例: 組み込まれたクエリ式を使用して XML ツリーの構造を変更する

次の例では、組み込まれたクエリ式を使用して、XML ファイルの構造を変更します。

この例のソース XML ドキュメント「サンプル XML ファイル: 顧客と注文」では、Root 要素の下にある Customers 要素にすべての顧客が含まれています。 また、Orders 要素の下には Root 要素もあり、すべての注文が含まれています。 例では、各顧客の注文が Orders 要素内の Customer 要素に含まれるような、新しい XML ツリーを作成します。 元のドキュメントでは、CustomerID 要素内に Order 要素も含まれています。この要素は新しいツリーから除外されます。

XElement co = XElement.Load("CustomersOrders.xml");
XElement newCustOrd =
    new XElement("Root",
        from cust in co.Element("Customers").Elements("Customer")
        select new XElement("Customer",
            cust.Attributes(),
            cust.Elements(),
            new XElement("Orders",
                from ord in co.Element("Orders").Elements("Order")
                where (string)ord.Element("CustomerID") == (string)cust.Attribute("CustomerID")
                select new XElement("Order",
                    ord.Attributes(),
                    ord.Element("EmployeeID"),
                    ord.Element("OrderDate"),
                    ord.Element("RequiredDate"),
                    ord.Element("ShipInfo")
                )
            )
        )
    );
Console.WriteLine(newCustOrd);
Dim co As XElement = XElement.Load("CustomersOrders.xml")
Dim newCustOrd = _
   <Root>
       <%= From cust In co.<Customers>.<Customer> _
           Select _
           <Customer>
               <%= cust.Attributes() %>
               <%= cust.Elements() %>
               <Orders>
                   <%= From ord In co.<Orders>.<Order> _
                       Where ord.<CustomerID>.Value = cust.@CustomerID _
                       Select _
                       <Order>
                           <%= ord.Attributes() %>
                           <%= ord.<EmployeeID> %>
                           <%= ord.<OrderDate> %>
                           <%= ord.<RequiredDate> %>
                           <%= ord.<ShipInfo> %>
                       </Order> _
                   %>
               </Orders>
           </Customer> _
       %>
   </Root>
Console.WriteLine(newCustOrd)

この例を実行すると、次の出力が生成されます。

<Root>
  <Customer CustomerID="GREAL">
    <CompanyName>Great Lakes Food Market</CompanyName>
    <ContactName>Howard Snyder</ContactName>
    <ContactTitle>Marketing Manager</ContactTitle>
    <Phone>(503) 555-7555</Phone>
    <FullAddress>
      <Address>2732 Baker Blvd.</Address>
      <City>Eugene</City>
      <Region>OR</Region>
      <PostalCode>97403</PostalCode>
      <Country>USA</Country>
    </FullAddress>
    <Orders />
  </Customer>
  <Customer CustomerID="HUNGC">
    <CompanyName>Hungry Coyote Import Store</CompanyName>
    <ContactName>Yoshi Latimer</ContactName>
    <ContactTitle>Sales Representative</ContactTitle>
    <Phone>(503) 555-6874</Phone>
    <Fax>(503) 555-2376</Fax>
    <FullAddress>
      <Address>City Center Plaza 516 Main St.</Address>
      <City>Elgin</City>
      <Region>OR</Region>
      <PostalCode>97827</PostalCode>
      <Country>USA</Country>
    </FullAddress>
    <Orders />
  </Customer>
  ...
</Root>

例: 構造がソース ドキュメントの構造とは異なるドキュメントを作成する

この例では、一部の要素の名前を変更し、一部の属性を要素に変換します。 ConvertAddress が呼び出され、XElement オブジェクトの一覧が返されます。 メソッドの引数は、Address 属性の値が Type である "Shipping" 複合要素を特定するクエリです。 この例では、XML ドキュメント「サンプル XML ファイル: 一般的な購買発注書」を使用します。

static IEnumerable<XElement> ConvertAddress(XElement add)
{
    List<XElement> fragment = new List<XElement>() {
        new XElement("NAME", (string)add.Element("Name")),
        new XElement("STREET", (string)add.Element("Street")),
        new XElement("CITY", (string)add.Element("City")),
        new XElement("ST", (string)add.Element("State")),
        new XElement("POSTALCODE", (string)add.Element("Zip")),
        new XElement("COUNTRY", (string)add.Element("Country"))
    };
    return fragment;
}

static void Main(string[] args)
{
    XElement po = XElement.Load("PurchaseOrder.xml");
    XElement newPo = new XElement("PO",
        new XElement("ID", (string)po.Attribute("PurchaseOrderNumber")),
        new XElement("DATE", (DateTime)po.Attribute("OrderDate")),
        ConvertAddress(
            (from el in po.Elements("Address")
            where (string)el.Attribute("Type") == "Shipping"
            select el)
            .First()
        )
    );
    Console.WriteLine(newPo);
}
Function ConvertAddress(ByVal add As XElement) As IEnumerable(Of XElement)
    Dim fragment = New List(Of XElement)
    fragment.Add(<NAME><%= add.<Name>.Value %></NAME>)
    fragment.Add(<STREET><%= add.<Street>.Value %></STREET>)
    fragment.Add(<CITY><%= add.<City>.Value %></CITY>)
    fragment.Add(<ST><%= add.<State>.Value %></ST>)
    fragment.Add(<POSTALCODE><%= add.<Zip>.Value %></POSTALCODE>)
    fragment.Add(<COUNTRY><%= add.<Country>.Value %></COUNTRY>)
    Return fragment
End Function

Sub Main()
    Dim po As XElement = XElement.Load("PurchaseOrder.xml")
    Dim newPo As XElement = _
        <PO>
            <ID><%= po.@PurchaseOrderNumber %></ID>
            <DATE><%= CDate(po.@OrderDate) %></DATE>
            <%= _
                ConvertAddress( _
                (From el In po.<Address> _
                Where el.@Type = "Shipping" _
                Select el) _
                .First() _
                ) _
            %>
        </PO>
    Console.WriteLine(newPo)
End Sub

この例を実行すると、次の出力が生成されます。

<PO>
  <ID>99503</ID>
  <DATE>1999-10-20T00:00:00</DATE>
  <NAME>Ellen Adams</NAME>
  <STREET>123 Maple Street</STREET>
  <CITY>Mill Valley</CITY>
  <ST>CA</ST>
  <POSTALCODE>10999</POSTALCODE>
  <COUNTRY>USA</COUNTRY>
</PO>

関連項目