XMLドキュメントを逆シリアル化する方法


472

このXMLドキュメントを逆シリアル化する方法:

<?xml version="1.0" encoding="utf-8"?>
<Cars>
  <Car>
    <StockNumber>1020</StockNumber>
    <Make>Nissan</Make>
    <Model>Sentra</Model>
  </Car>
  <Car>
    <StockNumber>1010</StockNumber>
    <Make>Toyota</Make>
    <Model>Corolla</Model>
  </Car>
  <Car>
    <StockNumber>1111</StockNumber>
    <Make>Honda</Make>
    <Model>Accord</Model>
  </Car>
</Cars>

私はこれを持っています:

[Serializable()]
public class Car
{
    [System.Xml.Serialization.XmlElementAttribute("StockNumber")]
    public string StockNumber{ get; set; }

    [System.Xml.Serialization.XmlElementAttribute("Make")]
    public string Make{ get; set; }

    [System.Xml.Serialization.XmlElementAttribute("Model")]
    public string Model{ get; set; }
}

[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
    [XmlArrayItem(typeof(Car))]
    public Car[] Car { get; set; }

}

public class CarSerializer
{
    public Cars Deserialize()
    {
        Cars[] cars = null;
        string path = HttpContext.Current.ApplicationInstance.Server.MapPath("~/App_Data/") + "cars.xml";

        XmlSerializer serializer = new XmlSerializer(typeof(Cars[]));

        StreamReader reader = new StreamReader(path);
        reader.ReadToEnd();
        cars = (Cars[])serializer.Deserialize(reader);
        reader.Close();

        return cars;
    }
}

それはうまくいかないようです:-(


サンプルドキュメントの山かっこをエスケープする必要があると思います。
harpo

4
この答えは本当に良いです:stackoverflow.com/a/19613934/196210
Revious

回答:


359

これが動作中のバージョンです。xmlでStockNumber、Make、Modelの値は要素ではなく属性であるため、XmlElementAttributeラベルをに変更しましたXmlElement。また、reader.ReadToEnd();(その関数はストリーム全体を読み取って文字列を返すため、Deserialize()関数はリーダーを使用できなくなりました...位置はストリームの最後にありました)も削除しました。私はまた、ネーミングに関していくつかの自由を取りました:)

ここにクラスがあります:

[Serializable()]
public class Car
{
    [System.Xml.Serialization.XmlElement("StockNumber")]
    public string StockNumber { get; set; }

    [System.Xml.Serialization.XmlElement("Make")]
    public string Make { get; set; }

    [System.Xml.Serialization.XmlElement("Model")]
    public string Model { get; set; }
}


[Serializable()]
[System.Xml.Serialization.XmlRoot("CarCollection")]
public class CarCollection
{
    [XmlArray("Cars")]
    [XmlArrayItem("Car", typeof(Car))]
    public Car[] Car { get; set; }
}

Deserialize関数:

CarCollection cars = null;
string path = "cars.xml";

XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));

StreamReader reader = new StreamReader(path);
cars = (CarCollection)serializer.Deserialize(reader);
reader.Close();

そして、わずかに微調整されたxml(<Cars>をラップするために新しい要素を追加する必要がありました... Netは配列の逆シリアル化にこだわっています):

<?xml version="1.0" encoding="utf-8"?>
<CarCollection>
<Cars>
  <Car>
    <StockNumber>1020</StockNumber>
    <Make>Nissan</Make>
    <Model>Sentra</Model>
  </Car>
  <Car>
    <StockNumber>1010</StockNumber>
    <Make>Toyota</Make>
    <Model>Corolla</Model>
  </Car>
  <Car>
    <StockNumber>1111</StockNumber>
    <Make>Honda</Make>
    <Model>Accord</Model>
  </Car>
</Cars>
</CarCollection>

68
[Serializable]を使用する場合、は冗長ですXmlSerializerXmlSerializer単にそれをチェックすることはありません。同様に、[Xml...]デフォルトの動作を模倣しているだけなので、ほとんどの属性は冗長です。つまり、デフォルトでは、呼び出されたプロパティStockNumberは名前の付いた要素として保存され<StockNumber>ます-そのための属性は必要ありません。
Marc Gravell

3
XmlElementAttribute = XmlElement(これは、サフィックス "Attribute"を省略できる言語機能です)に注意してください。実際の解決策は、ReadToEnd()呼び出しの削除とルートノードの追加です。しかし、質問を解決するerymskiのコードを使用する(与えられたxmlを解析する)
Flamefire

2
Kevinに感謝しますが、サンプルXMLからCarsCollectionを削除するとどうなりますか。クラスからCarscollectionを削除し、コードをDeserealizeしましたが、成功しませんでした。
Vikrant、2015

441

xmlをファイルに保存し、xsdを使用してC#クラスを生成するだけではどうですか。

  1. ファイルをディスクに書き込みます(foo.xmlという名前にしました)。
  2. xsdを生成します。 xsd foo.xml
  3. C#を生成します。 xsd foo.xsd /classes

Et voila-およびを介してデータを読み取ることができるはずのC#コードファイルXmlSerializer

    XmlSerializer ser = new XmlSerializer(typeof(Cars));
    Cars cars;
    using (XmlReader reader = XmlReader.Create(path))
    {
        cars = (Cars) ser.Deserialize(reader);
    }

(生成されたfoo.csをプロジェクトに含めます)


6
あなたは...男です!ありがとう。それを必要とするすべての人にとって、「パス」は次のようにWeb応答から作成するストリームにすることができます。var resp = response.Content.ReadAsByteArrayAsync(); var stream = new MemoryStream(resp.Result);
インダストラー2013

1
素晴らしいアイデアですが、入れ子になった配列のバッチを使用した、やや複雑なモデルでは正しく機能しませんでした。入れ子になった配列の型変換エラーが何度も発生しました-加えて、生成された名前付けスキームは、望ましいものを残しました。したがって、私はカスタムルートに行くことになりました。
GotDibbs 2013


2
xsd.exeは、windowsコマンドプロンプトではなく、visual studioコマンドプロンプトから使用できます。Visual Studioの[ツール]からコマンドプロンプトを開くことができるかどうかを確認します。そうでない場合は、visual studioフォルダーからアクセスしてみてください。VS 2012の場合は、C:\ Program Files(x86)\ Microsoft Visual Studio 12.0 \ Common7 \ Tools \ Shortcutsにあります。Windows 8で「Visual Studio Tools」を検索してみてください。
goku_da_master 2014年

2
XSDを探しているすべての人に。ここではSOのスレッドです:stackoverflow.com/questions/22975031/...
SOReader

229

2つの可能性があります。

方法1. XSDツール


この場所にXMLファイルがあるとします C:\path\to\xml\file.xml

  1. オープン開発者は、コマンドプロンプト
    あなたにはそれを見つけることができますStart Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools あなただけの入力を開始することができますWindows 8の持っている場合、または開発者のコマンドプロンプトでのスタート画面
  2. 次のように入力して、場所をXMLファイルディレクトリに変更します。 cd /D "C:\path\to\xml"
  3. 次のように入力して、xml ファイルからXSDファイルを作成します。xsd file.xml
  4. 入力してC#クラスを作成するxsd /c file.xsd

以上です!xmlファイルからC#クラスを生成しましたC:\path\to\xml\file.cs

方法2-特殊貼り付け


必要なVisual Studio 2012以降

  1. XMLファイルのコンテンツをクリップボードにコピーする
  2. ソリューションに新しい空のクラスファイルを追加します(Shift+ Alt+ C
  3. そのファイルを開き、メニューでクリックします Edit > Paste special > Paste XML As Classes
    ここに画像の説明を入力してください

以上です!

使用法


このヘルパークラスを使用すると、使用法は非常に簡単です。

using System;
using System.IO;
using System.Web.Script.Serialization; // Add reference: System.Web.Extensions
using System.Xml;
using System.Xml.Serialization;

namespace Helpers
{
    internal static class ParseHelpers
    {
        private static JavaScriptSerializer json;
        private static JavaScriptSerializer JSON { get { return json ?? (json = new JavaScriptSerializer()); } }

        public static Stream ToStream(this string @this)
        {
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(@this);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }


        public static T ParseXML<T>(this string @this) where T : class
        {
            var reader = XmlReader.Create(@this.Trim().ToStream(), new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
            return new XmlSerializer(typeof(T)).Deserialize(reader) as T;
        }

        public static T ParseJSON<T>(this string @this) where T : class
        {
            return JSON.Deserialize<T>(@this.Trim());
        }
    }
}

あなたが今しなければならないすべては、です:

    public class JSONRoot
    {
        public catalog catalog { get; set; }
    }
    // ...

    string xml = File.ReadAllText(@"D:\file.xml");
    var catalog1 = xml.ParseXML<catalog>();

    string json = File.ReadAllText(@"D:\file.json");
    var catalog2 = json.ParseJSON<JSONRoot>();

16
+1良い答え。ただし、Paste XML As Classesコマンドは.NET 4.5のみを対象としています
ravy amiry 2013年

1
vs2012 +がインストールされている場合、これはモデルを生成する優れた方法です。その後、ReSharperのコードクリーンアップを実行して自動プロパティを使用し、その他の整頓も行いました。この方法で生成し、必要に応じて古いプロジェクトにコピーできます。
Scotty.NET 2014年

4
.net4.5をターゲットにすることは問題ではありません。dotnet4.5を使用して一時プロジェクトを起動し、そこにコピーして貼り付け、ソースを実際のプロジェクトにコピーします。
LosManos 2015年

2
「カタログ」オブジェクトまたはクラスはどこにありますか?
CB4

3
「クラスとしてXMLを貼り付け」をVS 2017 Communityのメニューに表示するには、「ASP.NETとWeb開発」をインストールする必要があります。見つからない場合は、VSインストーラーを再度実行して、インストールを変更します。
Slion

89

次のスニペットでうまくいくはずです(そして、シリアライゼーション属性のほとんどは無視できます)。

public class Car
{
  public string StockNumber { get; set; }
  public string Make { get; set; }
  public string Model { get; set; }
}

[XmlRootAttribute("Cars")]
public class CarCollection
{
  [XmlElement("Car")]
  public Car[] Cars { get; set; }
}

...

using (TextReader reader = new StreamReader(path))
{
  XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
  return (CarCollection) serializer.Deserialize(reader);
}

14
これが実際に唯一の答えです。受け入れられた回答には、初心者を混乱させる可能性があるいくつかの欠陥があります。
Flamefire、2014年

24

これが役立つかどうかを確認してください:

[Serializable()]
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
    [XmlArrayItem(typeof(Car))]
    public Car[] Car { get; set; }
}

[Serializable()]
public class Car
{
    [System.Xml.Serialization.XmlElement()]
    public string StockNumber{ get; set; }

    [System.Xml.Serialization.XmlElement()]
    public string Make{ get; set; }

    [System.Xml.Serialization.XmlElement()]
    public string Model{ get; set; }
}

失敗すると、Visual Studioに付属のxsd.exeプログラムを使用して、そのxmlファイルに基づいてスキーマドキュメントを作成し、それを再度使用して、スキーマドキュメントに基づいてクラスを作成します。


9

.netが「配列の逆シリアル化について注意が必要」とは思わない。最初のxmlドキュメントは整形式ではありません。ルート要素はあるように見えますが、ありません。正規xmlドキュメントには、ルートと少なくとも1つの要素(ある場合)があります。あなたの例では:

<Root> <-- well, the root
  <Cars> <-- an element (not a root), it being an array
    <Car> <-- an element, it being an array item
    ...
    </Car>
  </Cars>
</Root>

7

.xmlファイルがディスクのどこかに生成されていて、次のコードを使用している場合は、このコードブロックを試してくださいList<T>

//deserialization

XmlSerializer xmlser = new XmlSerializer(typeof(List<Item>));
StreamReader srdr = new StreamReader(@"C:\serialize.xml");
List<Item> p = (List<Item>)xmlser.Deserialize(srdr);
srdr.Close();`

注:C:\serialize.xmlは.xmlファイルのパスです。必要に応じて変更できます。


6

Kevinのanserは優れていますが、実際には、ニーズに合わせて元のXMLを変更できないことがよくあります。

元のXMLにも簡単な解決策があります。

[XmlRoot("Cars")]
public class XmlData
{
    [XmlElement("Car")]
    public List<Car> Cars{ get; set; }
}

public class Car
{
    public string StockNumber { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}

そして、あなたは単に呼び出すことができます:

var ser = new XmlSerializer(typeof(XmlData));
XmlData data = (XmlData)ser.Deserialize(XmlReader.Create(PathToCarsXml));

ありがとう!ギガバイト相当のログファイルを変更したくなかったので、あなたの答えはまさに私が必要としたものです。
コリン

XmlSerializerソリューションは非常にエレガントですが、確かに非常に高速ではなく、予期しないXmlデータに敏感に反応することは言及する価値があります。したがって、問題が完全な逆シリアル化を必要としない場合は、より実用的でパフォーマンスの高いXmlReaderクラスのみを使用して、<Car>要素をループ処理することを検討する必要があります。
キム・ホーマン2018年

5

Xmlシリアライゼーションとデシリアライゼーションのこのジェネリッククラスを試してください。

public class SerializeConfig<T> where T : class
{
    public static void Serialize(string path, T type)
    {
        var serializer = new XmlSerializer(type.GetType());
        using (var writer = new FileStream(path, FileMode.Create))
        {
            serializer.Serialize(writer, type);
        }
    }

    public static T DeSerialize(string path)
    {
        T type;
        var serializer = new XmlSerializer(typeof(T));
        using (var reader = XmlReader.Create(path))
        {
            type = serializer.Deserialize(reader) as T;
        }
        return type;
    }
}

4

初心者向け

私はここでの回答が非常に役立つことがわかりました、それは私がまだこれを機能させるために(ほんの少し)苦労していたと言いました。だから、それが誰かを助ける場合に備えて、私が実用的な解決策を詳しく説明します:

元の質問からのXML。xmlはファイルClass1.xmlにありpathます。このファイルへのaは、このxmlファイルを見つけるためにコードで使用されます。

私は@erymskiの回答を使用してこれを機能させるため、Car.csというファイルを作成し、以下を追加しました。

using System.Xml.Serialization;  // Added

public class Car
{
    public string StockNumber { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}

[XmlRootAttribute("Cars")]
public class CarCollection
{
    [XmlElement("Car")]
    public Car[] Cars { get; set; }
}

@erymskiが提供するもう1つのコード...

using (TextReader reader = new StreamReader(path))
{
  XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
  return (CarCollection) serializer.Deserialize(reader);
}

...次のように、メインプログラム(Program.cs)に入りますstatic CarCollection XCar()

using System;
using System.IO;
using System.Xml.Serialization;

namespace ConsoleApp2
{
    class Program
    {

        public static void Main()
        {
            var c = new CarCollection();

            c = XCar();

            foreach (var k in c.Cars)
            {
                Console.WriteLine(k.Make + " " + k.Model + " " + k.StockNumber);
            }
            c = null;
            Console.ReadLine();

        }
        static CarCollection XCar()
        {
            using (TextReader reader = new StreamReader(@"C:\Users\SlowLearner\source\repos\ConsoleApp2\ConsoleApp2\Class1.xml"))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
                return (CarCollection)serializer.Deserialize(reader);
            }
        }
    }
}

それが役に立てば幸い :-)


1
それは私のために働いた。これは、(OPの例のように)指定されたxml入力に対しても完全に機能するソリューションです。[XmlElement( "Car")]は正しい属性です。他の例では、プロパティがpublic Car [] Cars {get;として定義されている限り不要なXmlArrayなどを使用しました。セットする; }そしてそれはそれを正しくデシリアライズします。ありがとう。
Diwakar Padmaraja

3

XMLドキュメントを逆シリアル化するジェネリッククラスはどうでしょう

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Generic class to load any xml into a class
// used like this ...
// YourClassTypeHere InfoList = LoadXMLFileIntoClass<YourClassTypeHere>(xmlFile);

using System.IO;
using System.Xml.Serialization;

public static T LoadXMLFileIntoClass<T>(string xmlFile)
{
    T returnThis;
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    if (!FileAndIO.FileExists(xmlFile))
    {
        Console.WriteLine("FileDoesNotExistError {0}", xmlFile);
    }
    returnThis = (T)serializer.Deserialize(new StreamReader(xmlFile));
    return (T)returnThis;
}

この部分は必要な場合と必要でない場合があります。Visual StudioでXMLドキュメントを開き、XMLを右クリックして、プロパティを選択します。次に、スキーマファイルを選択します。


1
これにより、ビジネスロジックコードをかなり縮小し、生成したすべての<T>クラスを使用してヘルパークラスに機能を集中させることができました。私はすでに文字列にXMLがあったので、これを圧縮することができます: `public static T LoadXMLFileIntoClass <T>(string xmlData)` {`XmlSerializer serializer = new XmlSerializer(typeof(T)); `return(T)serializer.Deserialize(new StringReader(xmlData)); `}ありがとう!
pwrgreg007

3

一発ギャグ:

var object = (Cars)new XmlSerializer(typeof(Cars)).Deserialize(new StringReader(xmlString));

2

逆シリアル化のためにすべてのレベルを処理するという考えです。私の同様の問題を解決したサンプルソリューションを参照してください

<?xml version="1.0" ?> 
 <TRANSACTION_RESPONSE>
    <TRANSACTION>
        <TRANSACTION_ID>25429</TRANSACTION_ID> 
        <MERCHANT_ACC_NO>02700701354375000964</MERCHANT_ACC_NO> 
        <TXN_STATUS>F</TXN_STATUS> 
        <TXN_SIGNATURE>a16af68d4c3e2280e44bd7c2c23f2af6cb1f0e5a28c266ea741608e72b1a5e4224da5b975909cc43c53b6c0f7f1bbf0820269caa3e350dd1812484edc499b279</TXN_SIGNATURE> 
        <TXN_SIGNATURE2>B1684258EA112C8B5BA51F73CDA9864D1BB98E04F5A78B67A3E539BEF96CCF4D16CFF6B9E04818B50E855E0783BB075309D112CA596BDC49F9738C4BF3AA1FB4</TXN_SIGNATURE2> 
        <TRAN_DATE>29-09-2015 07:36:59</TRAN_DATE> 
        <MERCHANT_TRANID>150929093703RUDZMX4</MERCHANT_TRANID> 
        <RESPONSE_CODE>9967</RESPONSE_CODE> 
        <RESPONSE_DESC>Bank rejected transaction!</RESPONSE_DESC> 
        <CUSTOMER_ID>RUDZMX</CUSTOMER_ID> 
        <AUTH_ID /> 
        <AUTH_DATE /> 
        <CAPTURE_DATE /> 
        <SALES_DATE /> 
        <VOID_REV_DATE /> 
        <REFUND_DATE /> 
        <REFUND_AMOUNT>0.00</REFUND_AMOUNT> 
    </TRANSACTION>
  </TRANSACTION_RESPONSE> 

上記のXMLは2つのレベルで処理されます

  [XmlType("TRANSACTION_RESPONSE")]
public class TransactionResponse
{
    [XmlElement("TRANSACTION")]
    public BankQueryResponse Response { get; set; }

}

内部レベル

public class BankQueryResponse
{
    [XmlElement("TRANSACTION_ID")]
    public string TransactionId { get; set; }

    [XmlElement("MERCHANT_ACC_NO")]
    public string MerchantAccNo { get; set; }

    [XmlElement("TXN_SIGNATURE")]
    public string TxnSignature { get; set; }

    [XmlElement("TRAN_DATE")]
    public DateTime TranDate { get; set; }

    [XmlElement("TXN_STATUS")]
    public string TxnStatus { get; set; }


    [XmlElement("REFUND_DATE")]
    public DateTime RefundDate { get; set; }

    [XmlElement("RESPONSE_CODE")]
    public string ResponseCode { get; set; }


    [XmlElement("RESPONSE_DESC")]
    public string ResponseDesc { get; set; }

    [XmlAttribute("MERCHANT_TRANID")]
    public string MerchantTranId { get; set; }

}

同じ方法で、マルチレベルの逆シリアル化のこの例確認して、マルチレベルが必要ですcar as array


1

xsd.exeを使用してxsdファイルを作成するときにエラーが発生する場合は、msdnで説明されているXmlSchemaInferenceクラスを使用します。デモする単体テストを次に示します。

using System.Xml;
using System.Xml.Schema;

[TestMethod]
public void GenerateXsdFromXmlTest()
{
    string folder = @"C:\mydir\mydata\xmlToCSharp";
    XmlReader reader = XmlReader.Create(folder + "\some_xml.xml");
    XmlSchemaSet schemaSet = new XmlSchemaSet();
    XmlSchemaInference schema = new XmlSchemaInference();

    schemaSet = schema.InferSchema(reader);


    foreach (XmlSchema s in schemaSet.Schemas())
    {
        XmlWriter xsdFile = new XmlTextWriter(folder + "\some_xsd.xsd", System.Text.Encoding.UTF8);
        s.Write(xsdFile);
        xsdFile.Close();
    }
}

// now from the visual studio command line type: xsd some_xsd.xsd /classes

1

Cars carプロパティの1つの属性をXmlArrayItemからXmlElmentに変更するだけです。つまり、

[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
    [XmlArrayItem(typeof(Car))]
    public Car[] Car { get; set; }
}

[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
    [XmlElement("Car")]
    public Car[] Car { get; set; }
}

1

私の解決策:

  1. Edit > Past Special > Paste XML As Classesコードでクラスを取得するために使用します
  2. 次のようなことを試してください。そのクラスのリスト(List<class1>)を作成し、を使用しXmlSerializerてそのリストをxmlファイルにシリアル化します。
  3. 今、あなたはあなたのデータでそのファイルの本体を置き換えて、deserializeそれを試してください。

コード:

StreamReader sr = new StreamReader(@"C:\Users\duongngh\Desktop\Newfolder\abc.txt");
XmlSerializer xml = new XmlSerializer(typeof(Class1[]));
var a = xml.Deserialize(sr);
sr.Close();

注:ルート名に注意を払う必要があります。変更しないでください。鉱山は「ArrayOfClass1」です


1
async public static Task<JObject> XMLtoNETAsync(XmlDocument ToConvert)
{
    //Van XML naar JSON
    string jsonText = await Task.Run(() => JsonConvert.SerializeXmlNode(ToConvert));

    //Van JSON naar .net object
    var o = await Task.Run(() => JObject.Parse(jsonText));

    return o;
}

1
単にコードを貼り付けるのではなく、常に状況に応じて答えを入力してください。詳細はこちらをご覧ください。
gehbiszumeis
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.