質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

4回答

596閲覧

XMLのシリアライズの際に構造がおかしくなってしまいます。

t.sei

総合スコア9

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2019/05/14 23:55

前提・実現したいこと

C#にてXMLファイルからデシリアライズした内容を、別ファイルに同内容、同構造でシリアライズしたいです。
XMLファイルは以下の同要素名の階層が、繰り返し入れ子となっています。

XML

1<main> 2 <products> 3 <product name=pan> 4 <substance name=komugi> 5 </substance> 6 <substance name=sio> 7 </substance> 8 </product> 9 <product name=gohan> 10 <substance name=kome> 11 </substance> 12 <substance name=mizu> 13 </substance> 14 </product> 15 </products> 16</main>

発生している問題・エラーメッセージ

シリアライズして出力した際に階層がおかしくなります。現状の出力結果は以下の通りです。

XML

1<main> 2 <products> 3 <product name=pan> 4 <substance name=komugi> 5 </substance> 6 <substance name=sio> 7 </substance> 8 <substance name=kome> 9 </substance> 10 <substance name=mizu> 11 </substance> 12 </product> 13 <product name=gohan> 14 </product> 15 </products> 16</main>

該当のソースコード

現状のソースコードになります。

C#

1var output_xml = XDocument.Load(output_xml_filename).Element("main"); 2 3var xml = XDocument.Load(xml_filename).Element("main"); 4 5foreach (var main in xml.Elements("main")) 6{ 7 foreach(var products in main.Elements("products")) 8 { 9 output_xml.Add(new XElement("products")); 10 var output_products = output_xml.Element("products"); 11 12 foreach(var product in products.Elements("product")) 13 { 14 product_name = product.Attribute("name").Value; 15 output_products.Add(new XElement("product" 16 , new XAttribute("name", product_name))); 17 var output_products_product = output_products.Element("product"); 18 19 foreach(var substance in product.Elements("substance")) 20 { 21 substance_name = substance.Attribute("name").Value; 22 output_products_product.Add(new XElement("substance" 23 , new XAttribute("name", substance_name))); 24 } 25 } 26 } 27}

試したこと

すみません。他の方法が思いつかず手詰まりになってしまっています。何卒ご教授頂きたいです。

補足情報(FW/ツールのバージョンなど)

開発はVisualStadio2017です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

こんにちは。

同内容同構造と言ってる以上、Zuishin さんの回答が大正解です。開いた xml を中身を見ることもせず output_xml に書くだけで良いのですから。

必要あるかはわかりませんが、現状のコードにおいて問題が起きた原因を示すと、

csharp

1 output_products.Add(new XElement("product" 2 , new XAttribute("name", product_name))); 3 var output_products_product = output_products.Element("product");

ここで output_productsproduct という同名の element を複数追加しているため、直後の output_products.Element("product") で element を取得したときに必ず「最初の一つ」が手に入るため、その後 output_products_product に要素を追加すると、1つ目の product に全ての substance が追加されるのです。

csharp

1 var output_products_product = new XElement("product", new XAttribute("name", product_name))); 2 output_products.Add(output_products_product);

こうするだけで問題は解決します。output_products_product は常に新しく作った element になるためです。

投稿2019/05/15 00:55

tamoto

総合スコア4105

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t.sei

2019/05/15 01:01

ありがとうございます。そういうことだったのですね。 理解が進みました。今後に役立たせていただきます。
guest

0

xml.Save(ファイル名)

追記

以下のコードは XMLFile1.xml を読み込み、pan の子要素として yeast を追加し、out.xml に書き込みます。

C#

1using System; 2using System.IO; 3using System.Linq; 4using System.Xml.Linq; 5 6namespace ConsoleApp1 7{ 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 string dir = AppDomain.CurrentDomain.BaseDirectory; 13 string xml_filename = Path.Combine(dir, "XMLFile1.xml"); 14 string output_xml_filename = Path.Combine(dir, "out.xml"); 15 var xml = XDocument.Load(xml_filename); 16 var output_xml = XDocument.Parse(xml.ToString()); 17 var pan = output_xml 18 .Root 19 .Element("products") 20 .Elements("product") 21 .Where(a => (string)a.Attribute("name") == "pan") 22 .First(); 23 pan.Add(new XElement("substance", new XAttribute("name", "yeast"))); 24 output_xml.Save(output_xml_filename); 25 Console.WriteLine(output_xml); 26 Console.ReadKey(); 27 } 28 } 29} 30

投稿2019/05/15 00:04

編集2019/05/15 01:26
Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t.sei

2019/05/15 00:08

申し訳ありません。記述が抜けていました。 xml.SaveはForeachループをすべて抜けた後記述しています。
Zuishin

2019/05/15 00:09

output_xml ではなく xml を保存すればいいではありませんか。
Zuishin

2019/05/15 00:11

保存の話ではなく、オブジェクトをコピーしたいのなら、var output_xml = XDocument.Parse(xml.ToString()); が簡単です。
t.sei

2019/05/15 00:43

ありがとうございます。単純にコピーするわけではなかった為に自分で記述の必要がありました。 質問内容が簡素すぎて誤解を生んでしまい申し訳ございませんでした。
Zuishin

2019/05/15 01:06 編集

単純にコピーした後で必要なところだけ変更すればいいではありませんか。
t.sei

2019/05/15 01:32

ありがとうございます。処理内容は複数の同構造(階層数はバラバラ)をもつXMLファイルを一つのXMLファイルとする処理でして、そのなかでシリアライズがうまくできずにおりました。はじめてXMLファイルを扱うコードを記述しなければならず、理解が進まずにいたところです。ひとつひとつ手探りだったため、最初につまずいた箇所を質問させて頂きました。他にもやり方がいろいろあるとは思うのですが、思いつかずにいたところであります。何度もコメント頂きましてありがたく思います。今後ともご指導のほどよろしくお願い申し上げます。
Zuishin

2019/05/15 01:35

やり方の一つのように言わないでください。 シリアライズには Save メソッドが用意されています。 せっかく LINQ to XML を使っているんですから、その機能を活用してください。
guest

0

もうベストアンサー出ているところに失礼しますが、
単にnewしたXElementをvarで受けてそれをAddすれば済む話に見えます。

※追記
申し訳ありません。質問者さんがよく分からないまま場当たり的な解決法を採ったように見えたのでスマホから慌てて書き込んだ結果こんな雑な回答になりました。。
tomato様が原因から適切な解決法までわかりやすく記述して下さっているのでそちらを参照してください。

投稿2019/05/15 00:52

編集2019/05/15 01:04
macof

総合スコア83

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t.sei

2019/05/15 01:01

ありがとうございます。今後に役立たせていただきます。
guest

0

ベストアンサー

もうすでに簡単な方法は出ていますが、もしも自分で処理を書く必要があるならば
var output_products_product = output_products.Element("product");

var output_products_product = output_products.Elements("product").Last();

投稿2019/05/15 00:34

moredeep

総合スコア1507

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t.sei

2019/05/15 00:43

ご教示ありがとうございます。目的の処理ができました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問