简体   繁体   English

如何反序列化 XML 文档

[英]How to Deserialize XML document

How do I Deserialize this XML document:我如何反序列化这个 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>

I have this:我有这个:

[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;
    }
}

that don't seem to work :-(这似乎不起作用:-(

How about you just save the xml to a file, and use xsd to generate C# classes?将xml 保存到文件中,然后使用xsd生成C# 类如何?

  1. Write the file to disk (I named it foo.xml)将文件写入磁盘(我将其命名为 foo.xml)
  2. Generate the xsd: xsd foo.xml生成xsd: xsd foo.xml
  3. Generate the C#: xsd foo.xsd /classes生成 C#: xsd foo.xsd /classes

Et voila - and C# code file that should be able to read the data via XmlSerializer :瞧 - 和 C# 代码文件应该能够通过XmlSerializer读取数据:

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

(include the generated foo.cs in the project) (在项目中包含生成的foo.cs)

Here's a working version.这是一个工作版本。 I changed the XmlElementAttribute labels to XmlElement because in the xml the StockNumber, Make and Model values are elements, not attributes.我改变了XmlElementAttribute标签XmlElement因为在StockNumber,品牌和型号值的元素,没有属性的XML。 Also I removed the reader.ReadToEnd();我也删除了reader.ReadToEnd(); (that function reads the whole stream and returns a string, so the Deserialize() function couldn't use the reader anymore...the position was at the end of the stream). (该函数读取整个流并返回一个字符串,因此Deserialize()函数不能再使用读取器......位置在流的末尾)。 I also took a few liberties with the naming :).我还对命名采取了一些自由:)。

Here are the classes:以下是课程:

[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; }
}

The Deserialize function:反序列化功能:

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();

And the slightly tweaked xml (I needed to add a new element to wrap <Cars>...Net is picky about deserializing arrays):和稍微调整的 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>

You have two possibilities.你有两种可能性。

Method 1. XSD tool方法一、 XSD工具


Suppose that you have your XML file in this location C:\\path\\to\\xml\\file.xml 假设您的 XML 文件位于此位置C:\\path\\to\\xml\\file.xml

  1. Open Developer Command Prompt打开开发人员命令提示符
    You can find it in Start Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools Or if you have Windows 8 can just start typing Developer Command Prompt in Start screen您可以在“ Start Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools找到它,或者如果您使用的是 Windows 8,则可以在“开始”屏幕中开始键入Developer Command Prompt
  2. Change location to your XML file directory by typing cd /D "C:\\path\\to\\xml"通过键入cd /D "C:\\path\\to\\xml"位置更改为您的 XML 文件目录
  3. Create XSD file from your xml file by typing xsd file.xml通过键入xsd file.xml从您的 xml 文件创建XSD 文件
  4. Create C# classes by typing xsd /c file.xsd通过键入xsd /c file.xsd创建C# 类

And that's it!就是这样! You have generated C# classes from xml file in C:\\path\\to\\xml\\file.cs您已经从C:\\path\\to\\xml\\file.cs xml 文件生成了 C# 类

Method 2 - Paste special方法 2 - 特殊粘贴


Required Visual Studio 2012+ 所需的 Visual Studio 2012+

  1. Copy content of your XML file to clipboard将 XML 文件的内容复制到剪贴板
  2. Add to your solution new, empty class file ( Shift + Alt + C )添加到您的解决方案新的空类文件( Shift + Alt + C
  3. Open that file and in menu click Edit > Paste special > Paste XML As Classes打开该文件,然后在菜单中单击“ Edit > Paste special > Paste XML As Classes
    在此处输入图片说明

And that's it!就是这样!

Usage用法


Usage is very simple with this helper class:这个助手类的用法非常简单:

 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()); } } }

All you have to do now, is:你现在要做的就是:

 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>();

The following snippet should do the trick (and you can ignore most of the serialization attributes):以下代码段应该可以解决问题(您可以忽略大多数序列化属性):

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);
}

See if this helps:看看这是否有帮助:

[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; }
}

And failing that use the xsd.exe program that comes with visual studio to create a schema document based on that xml file, and then use it again to create a class based on the schema document.如果失败,则使用 Visual Studio 附带的 xsd.exe 程序基于该 xml 文件创建架构文档,然后再次使用它创建基于架构文档的类。

I don't think .net is 'picky about deserializing arrays'.我不认为 .net '对反序列化数组很挑剔'。 The first xml document is not well formed.第一个 xml 文档格式不正确。 There is no root element, although it looks like there is.没有根元素,虽然看起来有。 The canonical xml document has a root and at least 1 element (if at all).规范的 xml 文档有一个根和至少 1 个元素(如果有的话)。 In your example:在你的例子中:

<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>

try this block of code if your .xml file has been generated somewhere in disk and if you have used List<T> :如果您的 .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();`

Note: C:\\serialize.xml is my .xml file's path.注意: C:\\serialize.xml是我的 .xml 文件的路径。 You can change it for your needs.您可以根据需要更改它。

Kevin's anser is good, aside from the fact, that in the real world, you are often not able to alter the original XML to suit your needs. Kevin 的 anser 很好,除此之外,在现实世界中,您通常无法更改原始 XML 以满足您的需要。

There's a simple solution for the original XML, too:原始 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; }
}

And then you can simply call:然后你可以简单地调用:

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

For Beginners初学者

I found the answers here to be very helpful, that said I still struggled (just a bit) to get this working.我发现这里的答案非常有帮助,也就是说我仍然努力(只是有点)让这个工作。 So, in case it helps someone I'll spell out the working solution:因此,如果它对某人有帮助,我将详细说明工作解决方案:

XML from Original Question.来自原始问题的 XML。 The xml is in a file Class1.xml, a path to this file is used in the code to locate this xml file.该 xml 位于文件 Class1.xml 中,该文件的path用于在代码中定位该 xml 文件。

I used the answer by @erymski to get this working, so created a file called Car.cs and added the following:我使用@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; } }

The other bit of code provided by @erymski ... @erymski 提供的另一段代码......

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

... goes into your main program (Program.cs), in static CarCollection XCar() like this: ...进入您的主程序 (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);
            }
        }
    }
}

Hope it helps :-)希望它有帮助:-)

Try this Generic Class For Xml Serialization & Deserialization.试试这个用于 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;
    }
}

一个班轮:

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

How about a generic class to deserialize an XML document如何使用通用类反序列化 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;
}

This part may, or may not be necessary.这部分可能是必要的,也可能不是。 Open the XML document in Visual Studio, right click on the XML, choose properties.在 Visual Studio 中打开 XML 文档,右键单击 XML,选择属性。 Then choose your schema file.然后选择您的架构文件。

The idea is to have all level being handled for deserialization Please see a sample solution that solved my similar issue这个想法是处理所有级别以进行反序列化请查看解决我类似问题的示例解决方案

<?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> 

The above XML is handled in two level上面的 XML 分两级处理

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

}

The Inner level内在层次

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; }

}

Same Way you need multiple level with car as array Check this example for multilevel deserialization同样的方式,您需要将car as array多级检查此示例以了解多级反序列化

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;
}

If you're getting errors using xsd.exe to create your xsd file, then use the XmlSchemaInference class as mentioned on msdn .如果您在使用 xsd.exe 创建 xsd 文件时遇到错误,请使用msdn 上提到的 XmlSchemaInference 类。 Here's a unit test to demonstrate:这是一个单元测试来演示:

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

You can just change one attribute for you Cars car property from XmlArrayItem to XmlElment.您只需将 Cars 汽车属性的一个属性从 XmlArrayItem 更改为 XmlElment。 That is, from也就是说,从

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

to

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

My solution:我的解决方案:

  1. Use Edit > Past Special > Paste XML As Classes to get the class in your code使用Edit > Past Special > Paste XML As Classes来获取代码中的类
  2. Try something like this: create a list of that class ( List<class1 >), then use the XmlSerializer to serialize that list to a xml file.尝试如下操作:创建该类的列表 ( List<class1 >),然后使用XmlSerializer将该列表序列化为xml文件。
  3. Now you just replace the body of that file with your data and try to deserialize it.现在您只需用您的数据替换该文件的正文并尝试对其进行deserialize

Code:代码:

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

NOTE: you must pay attention to the root name, don't change it.注意:一定要注意根名称,不要随意更改。 Mine is "ArrayOfClass1"我的是“ArrayOfClass1”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM