C Sharpの基礎 - XML

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

XML (Extensible Markup Language) は、データを構造化して保存するためのフォーマットである。
C#では、System.Xml名前空間を使用して、XMLファイルの読み書きや操作を行うことができる。

XMLファイルの基本的な構造は、ルート要素から始まり、その中に子要素やテキストノードが階層的に配置される。
各要素は開始タグと終了タグで囲まれ、属性を持つこともできる。
この構造により、データの意味や関係性を明確に表現することができる。

C#でXMLファイルを扱う場合によく使用されるクラスには、XmlReaderXmlWriterXmlDocumentXPath等がある。
XmlReaderクラスとXmlWriterクラスは高速で効率的なストリーミング処理を提供しており、大規模なXMLファイルの読み書きに適している。

一方、XmlDocumentクラスはXMLファイル全体をメモリ上に読み込み、ツリー構造として操作することができる。

XMLファイルの利用例としては、設定ファイル、データ交換、Webサービスの通信プロトコル (SOAP) 等が挙げられる。
特に、アプリケーションの設定情報をXMLファイルに保存することにより、プログラムの動作をコードを変更せずに柔軟に制御できるようになる。

また、LINQ to XMLを使用すると、XMLデータに対して強力なクエリや操作を行うことができる。
これにより、複雑なXML処理も直感的に記述することが可能になる。

※注意
大きなファイルを扱う場合のメモリ使用量や外部からのXMLインジェクション攻撃への対策等を考慮する必要がある。
また、適切なエラーハンドリングや入力のサニタイズも重要である。

C#におけるXMLファイルの扱いは、.NET Frameworkの進化とともに改善されており、より効率的で柔軟になってきている。


XMLファイルの作成

以下の例では、非同期処理とストリーミング処理を使用して、XmlWriterクラスで指定されたXML構造を持つXMLファイルを生成している。

 <!-- 作成するXMLファイルの構造 -->
 
 <Earthquake>
   <OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
   <ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
   <Hypocenter>
     <Area>
       <Name>茨城県南部</Name>
       <Code type="震央地名">301</Code>
     </Area>
   </Hypocenter>
   <jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
 </Earthquake>
 <Observation>
   <Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
     <Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
       <City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
         <IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
         <IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
       </City>
       <City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
         <IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
       </City>
     </Area>
   </Pref>
 </Observation>


 using System;
 using System.Xml;
 using System.Threading.Tasks;
 
 class Program
 {
    static async Task Main(string[] args)
    {
       string xmlFilePath = "sample.xml";
 
       try
       {
          await CreateEarthquakeXmlAsync(xmlFilePath);
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 
    static async Task CreateEarthquakeXmlAsync(string filePath)
    {
       var settings = new XmlWriterSettings
       {
          Async = true,
          Indent = true
       };
 
       using (var writer = XmlWriter.Create(filePath, settings))
       {
          await writer.WriteStartDocumentAsync();
 
          // ルート要素は記述せずに直接コンテンツを記述する
          await WriteEarthquakeElementAsync(writer);
          await WriteObservationElementAsync(writer);
 
          await writer.WriteEndDocumentAsync();
       }
    }
 
    static async Task WriteEarthquakeElementAsync(XmlWriter writer)
    {
       await writer.WriteStartElementAsync(null, "Earthquake", null);
 
       await writer.WriteElementStringAsync(null, "OriginTime", null, "2024-08-23T21:00:00+09:00");
       await writer.WriteElementStringAsync(null, "ArrivalTime", null, "2024-08-23T21:01:00+09:00");
 
       await writer.WriteStartElementAsync(null, "Hypocenter", null);
       await writer.WriteStartElementAsync(null, "Area", null);
       await writer.WriteElementStringAsync(null, "Name", null, "茨城県南部");
         
       await writer.WriteStartElementAsync(null, "Code", null);
       await writer.WriteAttributeStringAsync(null, "type", null, "震央地名");
       await writer.WriteStringAsync("301");
       await writer.WriteEndElementAsync();
 
       await writer.WriteEndElementAsync();  // Area
       await writer.WriteEndElementAsync();  // Hypocenter
 
       await writer.WriteStartElementAsync("jmx_eb", "Magnitude", null);
       await writer.WriteAttributeStringAsync(null, "type", null, "Mj");
       await writer.WriteAttributeStringAsync(null, "description", null, "M3.8");
       await writer.WriteStringAsync("3.8");
       await writer.WriteEndElementAsync();
 
       await writer.WriteEndElementAsync();  // Earthquake
    }
 
    static async Task WriteObservationElementAsync(XmlWriter writer)
    {
       await writer.WriteStartElementAsync(null, "Observation", null);
 
       await writer.WriteStartElementAsync(null, "Pref", null);
       await writer.WriteElementStringAsync(null, "Name", null, "茨城県");
       await writer.WriteElementStringAsync(null, "Code", null, "08");
       await writer.WriteElementStringAsync(null, "MaxInt", null, "2");
 
       await writer.WriteStartElementAsync(null, "Area", null);
       await writer.WriteElementStringAsync(null, "Name", null, "茨城県北部");
       await writer.WriteElementStringAsync(null, "Code", null, "300");
       await writer.WriteElementStringAsync(null, "MaxInt", null, "2");
 
       // 小美玉市
       await writer.WriteStartElementAsync(null, "City", null);
       await writer.WriteElementStringAsync(null, "Name", null, "小美玉市");
       await writer.WriteElementStringAsync(null, "Code", null, "0823600");
       await writer.WriteElementStringAsync(null, "MaxInt", null, "2");
 
       await WriteIntensityStationAsync(writer, "小美玉市小川*", "0823633", "2");
       await WriteIntensityStationAsync(writer, "小美玉市上玉里*", "0823635", "2");
 
       await writer.WriteEndElementAsync();  // City
 
       // 水戸市
       await writer.WriteStartElementAsync(null, "City", null);
       await writer.WriteElementStringAsync(null, "Name", null, "水戸市");
       await writer.WriteElementStringAsync(null, "Code", null, "0820100");
       await writer.WriteElementStringAsync(null, "MaxInt", null, "1");
 
       await WriteIntensityStationAsync(writer, "水戸市千波町*", "0820121", "1");
 
       await writer.WriteEndElementAsync();  // City
 
       await writer.WriteEndElementAsync();  // Area
       await writer.WriteEndElementAsync();  // Pref
       await writer.WriteEndElementAsync();  // Observation
    }
 
    static async Task WriteIntensityStationAsync(XmlWriter writer, string name, string code, string intensity)
    {
       await writer.WriteStartElementAsync(null, "IntensityStation", null);
       await writer.WriteElementStringAsync(null, "Name", null, name);
       await writer.WriteElementStringAsync(null, "Code", null, code);
       await writer.WriteElementStringAsync(null, "Int", null, intensity);
       await writer.WriteEndElementAsync();
    }
 }



要素の取得

以下の例では、非同期処理を使用してXMLファイルを読み込み、以下に示す要素を読み込んでいる。

  • <Hypocenter> -> <Area> -> <Name>の値
  • <Hypocenter> -> <Area> -> <Code>のtype属性の値
  • 全ての<Observation> -> <IntensityStation> -> <Name>の値


読み込むXMLファイルを以下に示す。

 <!-- 使用するXMLファイル -->
 
 <Earthquake>
   <OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
   <ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
   <Hypocenter>
     <Area>
       <Name>茨城県南部</Name>
       <Code type="震央地名">301</Code>
     </Area>
   </Hypocenter>
   <jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
 </Earthquake>
 <Observation>
   <Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
     <Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
       <City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
         <IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
         <IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
       </City>
       <City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
         <IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
       </City>
     </Area>
   </Pref>
 </Observation>


 using System;
 using System.IO;
 using System.Collections.Generic;
 using System.Xml;
 using System.Threading.Tasks;
 
 class Program
 {
    static async Task Main(string[] args)
    {
       string xmlFilePath = "sample.xml";  // XMLファイルのパスを指定
 
       try
       {
          var (hypocenterAreaName, hypocenterAreaCodeType, intensityStationNames) = await ParseXmlFileAsync(xmlFilePath);
 
          // 結果を出力
          Console.WriteLine($"震源地: {hypocenterAreaName}");
          Console.WriteLine($"震央地名コードタイプ: {hypocenterAreaCodeType}");
          Console.WriteLine("観測点:");
          foreach (var name in intensityStationNames)
          {
             Console.WriteLine($"- {name}");
          }
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 
    // XMLファイルを非同期で解析
    static async Task<(string hypocenterAreaName, string hypocenterAreaCodeType, List<string> intensityStationNames)> ParseXmlFileAsync(string filePath)
    {
       string hypocenterAreaName = null;
       string hypocenterAreaCodeType = null;
       var intensityStationNames = new List<string>();
 
       using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous))
       using (var reader = XmlReader.Create(stream, new XmlReaderSettings { Async = true }))
       {
          while (await reader.ReadAsync())
          {
             if (reader.NodeType == XmlNodeType.Element)
             {
                switch (reader.Name)
                {
                   case "Area":
                      // Hypocenter内のAreaを処理
                      if (reader.Depth == 3 && hypocenterAreaName == null)
                      {
                         (hypocenterAreaName, hypocenterAreaCodeType) = await ProcessHypocenterAreaAsync(reader);
                      }
                      break;
 
                   case "IntensityStation":
                      // IntensityStationを処理
                      await ProcessIntensityStationAsync(reader, intensityStationNames);
                      break;
                }
             }
          }
       }
 
       return (hypocenterAreaName, hypocenterAreaCodeType, intensityStationNames);
    }
 
    // Hypocenter内のArea要素を非同期で処理
    static async Task<(string areaName, string codeType)> ProcessHypocenterAreaAsync(XmlReader reader)
    {
       string areaName = null;
       string codeType = null;
 
       await reader.ReadAsync();  // Area要素の子に移動
 
       while (!(reader.NodeType == XmlNodeType.EndElement && reader.Name == "Area"))
       {
          if (reader.NodeType == XmlNodeType.Element)
          {
             switch (reader.Name)
             {
                case "Name":
                   areaName = await reader.ReadElementContentAsStringAsync();
                   break;
                case "Code":
                   codeType = reader.GetAttribute("type");
                   await reader.SkipAsync();  // Code要素をスキップ
                   break;
             }
          }
          else
          {
             await reader.ReadAsync();
          }
       }
 
       return (areaName, codeType);
    }
 
    // IntensityStation要素を非同期で処理
    static async Task ProcessIntensityStationAsync(XmlReader reader, List<string> stationNames)
    {
       await reader.ReadAsync();  // IntensityStation要素の子に移動
 
       while (!(reader.NodeType == XmlNodeType.EndElement && reader.Name == "IntensityStation"))
       {
          if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name")
          {
             stationNames.Add(await reader.ReadElementContentAsStringAsync());
          }
          else
          {
             await reader.ReadAsync();
          }
       }
    }
 }



要素の変更

以下の例では、非同期処理を使用してXMLファイルを読み込み、以下に示す要素を読み込んでいる。

  • <Hypocenter> -> <Area> -> <Name>の値をhogeに変更
  • <Hypocenter> -> <Area> -> <Code>のtype属性の値を"99に変更
  • 全ての<Observation> -> <IntensityStation> -> <Name>の値をpiyoに変更


読み込むXMLファイルを以下に示す。

 <!-- 変更前のXMLファイル -->
 
 <Earthquake>
   <OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
   <ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
   <Hypocenter>
     <Area>
       <Name>茨城県南部</Name>
       <Code type="震央地名">301</Code>
     </Area>
   </Hypocenter>
   <jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
 </Earthquake>
 <Observation>
   <Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
     <Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
       <City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
         <IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
         <IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
       </City>
       <City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
         <IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
       </City>
     </Area>
   </Pref>
 </Observation>


 using System;
 using System.IO;
 using System.Xml;
 using System.Threading.Tasks;
 
 class Program
 {
    static async Task Main(string[] args)
    {
       string inputXmlPath = "input.xml";
       string outputXmlPath = "output.xml";
 
       try
       {
          await ModifyXmlFileAsync(inputXmlPath, outputXmlPath);
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 
    static async Task ModifyXmlFileAsync(string inputPath, string outputPath)
    {
       var readerSettings = new XmlReaderSettings { Async = true };
       var writerSettings = new XmlWriterSettings { Async = true, Indent = true };
 
       using (var reader = XmlReader.Create(inputPath, readerSettings))
       using (var writer = XmlWriter.Create(outputPath, writerSettings))
       {
          await writer.WriteStartDocumentAsync();
 
          while (await reader.ReadAsync())
          {
             switch (reader.NodeType)
             {
                case XmlNodeType.Element:
                   await ProcessElementAsync(reader, writer);
                   break;
                case XmlNodeType.Text:
                   await writer.WriteStringAsync(reader.Value);
                   break;
                case XmlNodeType.XmlDeclaration:
                case XmlNodeType.ProcessingInstruction:
                   await writer.WriteProcessingInstructionAsync(reader.Name, reader.Value);
                   break;
                case XmlNodeType.Comment:
                   await writer.WriteCommentAsync(reader.Value);
                   break;
                case XmlNodeType.EndElement:
                   await writer.WriteEndElementAsync();
                   break;
                default:
                   await writer.WriteNodeAsync(reader, false);
                   break;
             }
          }
 
          await writer.WriteEndDocumentAsync();
       }
    }
 
    static async Task ProcessElementAsync(XmlReader reader, XmlWriter writer)
    {
       if (reader.Name == "Area" && reader.Depth == 3) // Hypocenter -> Area
       {
          await writer.WriteStartElementAsync(reader.Prefix, reader.LocalName, reader.NamespaceURI);
          await ModifyHypocenterAreaAsync(reader, writer);
       }
       else if (reader.Name == "IntensityStation")
       {
          await writer.WriteStartElementAsync(reader.Prefix, reader.LocalName, reader.NamespaceURI);
          await ModifyIntensityStationAsync(reader, writer);
       }
       else
       {
          await writer.WriteStartElementAsync(reader.Prefix, reader.LocalName, reader.NamespaceURI);
          if (reader.HasAttributes)
          {
             await WriteAttributesAsync(reader, writer);
          }
       }
    }
 
    static async Task ModifyHypocenterAreaAsync(XmlReader reader, XmlWriter writer)
    {
       while (await reader.ReadAsync())
       {
          if (reader.NodeType == XmlNodeType.Element)
          {
             if (reader.Name == "Name")
             {
                await writer.WriteElementStringAsync(reader.Prefix, "Name", reader.NamespaceURI, "hoge");
                await reader.ReadAsync(); // Skip the original content
             }
             else if (reader.Name == "Code")
             {
                await writer.WriteStartElementAsync(reader.Prefix, "Code", reader.NamespaceURI);
                await writer.WriteAttributeStringAsync(null, "type", null, "99");
                await writer.WriteStringAsync(await reader.ReadElementContentAsStringAsync());
                await writer.WriteEndElementAsync();
             }
             else
             {
                await writer.WriteNodeAsync(reader, false);
             }
          }
          else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Area")
          {
             break;
          }
       }
    }
 
    static async Task ModifyIntensityStationAsync(XmlReader reader, XmlWriter writer)
    {
       while (await reader.ReadAsync())
       {
          if (reader.NodeType == XmlNodeType.Element)
          {
             if (reader.Name == "Name")
             {
                await writer.WriteElementStringAsync(reader.Prefix, "Name", reader.NamespaceURI, "piyo");
                await reader.ReadAsync(); // Skip the original content
             }
             else
             {
                await writer.WriteNodeAsync(reader, false);
             }
          }
          else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "IntensityStation")
          {
             break;
          }
       }
    }
 
    static async Task WriteAttributesAsync(XmlReader reader, XmlWriter writer)
    {
       for (int i = 0; i < reader.AttributeCount; i++)
       {
          reader.MoveToAttribute(i);
          await writer.WriteAttributeStringAsync(reader.Prefix, reader.LocalName, reader.NamespaceURI, reader.Value);
       }
       reader.MoveToElement();
    }
 }


 <!-- 変更後のXMLファイル -->
 
 <?xml version="1.0" encoding="utf-8"?>
 <Earthquake>
   <OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
   <ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
   <Hypocenter>
     <Area>
       <Name>hoge</Name>
       <Code type="99">301</Code>
     </Area>
   </Hypocenter>
   <jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
 </Earthquake>
 <Observation>
   <Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
     <Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
       <City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
         <IntensityStation><Name>piyo</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
         <IntensityStation><Name>piyo</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
       </City>
       <City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
         <IntensityStation><Name>piyo</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
       </City>
     </Area>
   </Pref>
 </Observation>



LINQ to XMLの使用

XMLファイルの作成

以下の例では、LINQ to XMLを使用して、下記のXMLファイルを非同期で生成している。
非同期処理およびストリーミング処理を使用しているため、大きなXMLファイルを扱う場合でもメモリ効率が良く、アプリケーションの応答性を維持している。

  • GenerateEarthquakeXmlAsyncメソッド
    LINQ to XMLを使用してXMLドキュメントを構築する。
    指定されたXML構造に従って、全ての要素と属性を作成する。
    jmx_eb名前空間を使用して、特定の要素に名前空間を適用している。
  • SaveXmlToFileAsyncメソッド
    ストリーミング処理を使用して、XMLドキュメントをファイルに非同期で保存する。
    FileStreamクラスを使用して、非同期I/O操作を行う。


 <!-- 作成するXMLファイルの構造 -->
 
 <Earthquake>
   <OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
   <ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
   <Hypocenter>
     <Area>
       <Name>茨城県南部</Name>
       <Code type="震央地名">301</Code>
     </Area>
   </Hypocenter>
   <jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
 </Earthquake>
 <Observation>
   <Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
     <Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
       <City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
         <IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
         <IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
       </City>
       <City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
         <IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
       </City>
     </Area>
   </Pref>
 </Observation>


 using System;
 using System.IO;
 using System.Xml.Linq;
 using System.Threading.Tasks;
 
 class Program
 {
    static async Task Main(string[] args)
    {
       try
       {
          // XMLドキュメントを生成
          XDocument doc = await GenerateEarthquakeXmlAsync();
 
          // ファイルに非同期で保存
          await SaveXmlToFileAsync(doc, "sample.xml");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 
    static async Task<XDocument> GenerateEarthquakeXmlAsync()
    {
       // 非同期タスクをシミュレート
       await Task.Delay(100);
 
       // XMLの名前空間を定義
       XNamespace jmx_eb = "http://www.jma.go.jp/jmx-eb";
 
       // XMLドキュメントを作成
       XDocument doc = new XDocument(new XElement("Root",
                new XElement("Earthquake",
                    new XElement("OriginTime", "2024-08-23T21:00:00+09:00"),
                    new XElement("ArrivalTime", "2024-08-23T21:01:00+09:00"),
                    new XElement("Hypocenter",
                        new XElement("Area",
                            new XElement("Name", "茨城県南部"),
                            new XElement("Code", new XAttribute("type", "震央地名"), "301")
                        )
                    ),
                    new XElement(jmx_eb + "Magnitude",
                        new XAttribute("type", "Mj"),
                        new XAttribute("description", "M3.8"),
                        "3.8"
                    )
                ),
                new XElement("Observation",
                    new XElement("Pref",
                        new XElement("Name", "茨城県"),
                        new XElement("Code", "08"),
                        new XElement("MaxInt", "2"),
                        new XElement("Area",
                            new XElement("Name", "茨城県北部"),
                            new XElement("Code", "300"),
                            new XElement("MaxInt", "2"),
                            new XElement("City",
                                new XElement("Name", "小美玉市"),
                                new XElement("Code", "0823600"),
                                new XElement("MaxInt", "2"),
                                new XElement("IntensityStation",
                                    new XElement("Name", "小美玉市小川*"),
                                    new XElement("Code", "0823633"),
                                    new XElement("Int", "2")
                                ),
                                new XElement("IntensityStation",
                                    new XElement("Name", "小美玉市上玉里*"),
                                    new XElement("Code", "0823635"),
                                    new XElement("Int", "2")
                                )
                            ),
                            new XElement("City",
                                new XElement("Name", "水戸市"),
                                new XElement("Code", "0820100"),
                                new XElement("MaxInt", "1"),
                                new XElement("IntensityStation",
                                    new XElement("Name", "水戸市千波町*"),
                                    new XElement("Code", "0820121"),
                                    new XElement("Int", "1")
                                )
                            )
                        )
                    )
                )
            )
        );
 
       return doc;
    }
 
    static async Task SaveXmlToFileAsync(XDocument doc, string fileName)
    {
       // ストリーミング処理を使用してファイルに非同期で保存
       using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
       {
          await doc.SaveAsync(fs, SaveOptions.None, default);
       }
    }
 }


要素の取得

以下の例では、LINQ to XMLおよび非同期処理を使用してXMLファイルを読み込み、以下に示す要素を読み込んでいる。
また、大きなXMLファイルを効率的に処理するためのストリーミング処理も実装している。

LINQ to XMLを使用する場合は、System.Xml.Linq名前空間を追加する必要がある。

  • <Hypocenter> -> <Area> -> <Name>の値
  • <Hypocenter> -> <Area> -> <Code>のtype属性の値
  • 全ての<Observation> -> <IntensityStation> -> <Name>の値


  • LINQ to XML
    XDocumentクラスとDescendantsメソッド、Elementsメソッド、Attributesメソッドを使用して、XMLデータを効率的にクエリする。
  • ストリーミング処理
    ProcessLargeXmlFileAsyncメソッドでは、XmlReaderクラスを使用して大きなXMLファイルを効率的に処理する。
    これにより、メモリ使用量を抑えつつ、大規模なXMLファイルを扱うことができる。


 <!-- 使用するXMLファイルの構造 -->
 
 <Earthquake>
   <OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
   <ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
   <Hypocenter>
     <Area>
       <Name>茨城県南部</Name>
       <Code type="震央地名">301</Code>
     </Area>
   </Hypocenter>
   <jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
 </Earthquake>
 <Observation>
   <Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
     <Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
       <City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
         <IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
         <IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
       </City>
       <City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
         <IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
       </City>
     </Area>
   </Pref>
 </Observation>


 using System;
 using System.IO;
 using System.Linq;
 using System.Xml.Linq;
 using System.Collections.Generic;
 using System.Threading.Tasks;
 
 class Program
 {
    static async Task Main(string[] args)
    {
       try
       {
          // XMLファイルのパスを指定
          string xmlFilePath = "sample.xml";
 
          // XMLファイルを非同期で読み込み、XDocumentオブジェクトを生成
          XDocument doc = await Task.Run(() => XDocument.Load(xmlFilePath));
 
          // <Hypocenter> -> <Area> -> <Name>の値を取得
          string areaName = doc.Descendants("Hypocenter")
                               .Elements("Area")
                               .Elements("Name")
                               .FirstOrDefault()?.Value;
 
          Console.WriteLine($"震源地域名: {areaName}");
 
          // <Hypocenter> -> <Area> -> <Code>のtype属性の値を取得
          string codeType = doc.Descendants("Hypocenter")
                               .Elements("Area")
                               .Elements("Code")
                               .Attributes("type")
                               .FirstOrDefault()?.Value;
 
          Console.WriteLine($"コードタイプ: {codeType}");
 
          // 全ての<Observation> -> <IntensityStation> -> <Name>の値を取得
          var intensityStationNames = doc.Descendants("Observation")
                                         .Descendants("IntensityStation")
                                         .Elements("Name")
                                         .Select(e => e.Value)
                                         .ToList();
 
          Console.WriteLine("観測点名:");
          foreach (var name in intensityStationNames)
          {
             Console.WriteLine($"- {name}");
          }
 
          // ストリーミング処理を使用して大きなXMLファイルを効率的に処理する例
          await ProcessLargeXmlFileAsync(xmlFilePath);
       }
       catch (FileNotFoundException)
       {
          Console.WriteLine("エラー: 指定されたXMLファイルが存在しない");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラー: XMLの処理中に問題が発生  詳細: {ex.Message}");
       }
    }
 
    // 大きなXMLファイルを効率的にストリーミング処理する
    static async Task ProcessLargeXmlFileAsync(string filePath)
    {
       Console.WriteLine("ストリーミング処理による大規模XMLファイルの処理:");
 
       using (var reader = new StreamReader(filePath))
       {
          using (var xmlReader = System.Xml.XmlReader.Create(reader))
          {
             while (await xmlReader.ReadAsync())
             {
                if (xmlReader.NodeType == System.Xml.XmlNodeType.Element)
                {
                   if (xmlReader.Name == "IntensityStation")
                   {
                      // IntensityStationの子要素を処理
                      var element   = await XElement.ReadFromAsync(xmlReader);
                      var name      = element.Element("Name")?.Value;
                      var code      = element.Element("Code")?.Value;
                      var intensity = element.Element("Int")?.Value;
 
                      Console.WriteLine($"観測点: {name}, コード: {code}, 震度: {intensity}");
                   }
                }
             }
          }
       }
    }
 }


要素の変更

以下の例では、LINQ to XMLを使用して、以下に示すXMLファイルの一部の値を変更している。

  • ModifyXmlFileAsync メソッド:
    FileStreamクラスを使用して、XMLファイルを非同期で読み込む。
    LINQ to XMLを使用して、指定された要素と属性を検索して、値を変更する。
    変更後のXMLドキュメントを非同期で保存する。

  • 変更操作
    <Hypocenter> -> <Area> -> <Name>の値を"hoge"に変更する。
    <Hypocenter> -> <Area> -> <Code>のtype属性の値を"99"に変更する。
    全ての<Observation> -> <IntensityStation> -> <Name>の値を"piyo"に変更する。

  • 名前空間の処理
    XMLドキュメントのデフォルト名前空間を取得して、要素の検索に使用する。
  • 非同期処理
    ファイルの読み込みと保存に非同期メソッドを使用する。
  • ストリーミング処理
    FileStreamクラスを使用して、ファイルの読み込みと保存をストリーミング方式で行う。


 <!-- 変更前のXMLファイルの構造 -->
 
 <Earthquake>
   <OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
   <ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
   <Hypocenter>
     <Area>
       <Name>茨城県南部</Name>
       <Code type="震央地名">301</Code>
     </Area>
   </Hypocenter>
   <jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
 </Earthquake>
 <Observation>
   <Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
     <Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
       <City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
         <IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
         <IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
       </City>
       <City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
         <IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
       </City>
     </Area>
   </Pref>
 </Observation>


 using System;
 using System.IO;
 using System.Linq;
 using System.Xml.Linq;
 using System.Threading.Tasks;
 
 class Program
 {
    static async Task Main(string[] args)
    {
       try
       {
          // XMLファイルのパス
          string filePath = "sample.xml";
 
          // XMLファイルを非同期で読み込み、修正し、保存する
          await ModifyXmlFileAsync(filePath);
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 
    static async Task ModifyXmlFileAsync(string filePath)
    {
       // XMLファイルを非同期で読み込む
       XDocument doc;
       using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
       {
          doc = await XDocument.LoadAsync(fs, LoadOptions.None, default);
       }
 
       // XMLの名前空間を取得
       XNamespace ns = doc.Root.GetDefaultNamespace();
 
       // <Hypocenter> -> <Area> -> <Name>の値を変更
       var hypocenterAreaName = doc.Descendants(ns + "Hypocenter")
                                   .Elements(ns + "Area")
                                   .Elements(ns + "Name")
                                   .FirstOrDefault();
       if (hypocenterAreaName != null)
       {
          hypocenterAreaName.Value = "hoge";
       }
 
       // <Hypocenter> -> <Area> -> <Code>のtype属性の値を変更
       var hypocenterAreaCode = doc.Descendants(ns + "Hypocenter")
                                   .Elements(ns + "Area")
                                   .Elements(ns + "Code")
                                   .FirstOrDefault();
       if (hypocenterAreaCode != null)
       {
          hypocenterAreaCode.Attribute("type").Value = "99";
       }
 
       // 全ての<Observation> -> <IntensityStation> -> <Name>の値を変更
       var intensityStationNames = doc.Descendants(ns + "Observation")
                                      .Descendants(ns + "IntensityStation")
                                      .Elements(ns + "Name");
       foreach (var name in intensityStationNames)
       {
          name.Value = "piyo";
       }
 
       // 変更したXMLを非同期で保存
       using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
       {
          await doc.SaveAsync(fs, SaveOptions.None, default);
       }
    }
 }


 <!-- 変更後のXMLファイルの構造 -->
 
 <Earthquake>
   <OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
   <ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
   <Hypocenter>
     <Area>
       <Name>hoge</Name>
       <Code type="99">301</Code>
     </Area>
   </Hypocenter>
   <jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
 </Earthquake>
 <Observation>
   <Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
     <Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
       <City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
         <IntensityStation><Name>piyo</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
         <IntensityStation><Name>piyo</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
       </City>
       <City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
         <IntensityStation><Name>piyo</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
       </City>
     </Area>
   </Pref>
 </Observation>