【问题标题】:How to deserialize child object into list of parent (ex, List<Parent>) based on values of xml nodes?如何根据 xml 节点的值将子对象反序列化为父对象列表(例如,List<Parent>)?
【发布时间】:2019-08-27 04:33:07
【问题描述】:

我有一个名为 VehicleInfo 的 XML 文件。 我想反序列化车辆列表中的 VehicleInfo。 现在我有一个名为 Vehicle 的基类和三个名为 Car、Bike、Truck 的派生类。 如何根据xml中车辆节点的值反序列化车辆的特定对象。 (例如,如果节点值为 Car,则 car 的对象应存储在车辆列表中)

<Vehicles>
    <Vehicle>Car</Vehicle>
    <Vehicle>Bike</Vehicle>
    <Vehicle>Truck</Vehicle>
</Vehicles>

例如,

VehicleList 类:

public class VehicleList
{
    List<Vehicle> lstVehicles = new List<Vehicle>();
}

车辆类别:

public class Vehicle
{
    public string name = "Vehicle";
}

汽车类:

public class Car : Vehicle
{
    public Car()
    {
        name = "Car";
    }
}

自行车课:

public class Bike : Vehicle
{
    public Bike()
    {
        name = "Bike";
    }
}

卡车类:

public class Truck : Vehicle
{
    public Truck()
    {
        name = "Truck";
    }
}

此车辆程序只是示例,

那么,如何根据节点 Vehicle 的值反序列化 VehicleList 类中车辆列表中的特定对象(例如汽车、自行车或卡车)。

【问题讨论】:

  • 在反序列化之前制作样本并序列化以查看结构。您需要使用 Include 属性。见:
  • 嗨,欢迎来到 SO!为了获得帮助,也请提供您的类的结构。基类和子类(至少它们的一些基本字段)
  • 你尝试了什么?

标签: c# deserialization


【解决方案1】:

好的,这会很长......

你可以玩这个解决方案.net-fiddle-here

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Serialization;

public class Program
{
    public static void Main()
    {
        var root = new Vehicles
        {
            Items =
            {
                new Vehicle() { Name = "Car"},
                new Vehicle() { Name = "Truck"},
                new Vehicle() { Name = "Bike"}
            }
        };
        var xmlSerializer = new XmlSerializer(typeof(Vehicles));

        var memoryStream = new MemoryStream();
        TextWriter stringWriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8);
        xmlSerializer.Serialize(stringWriter, root);

        string xml = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray());


        //Make XML
        var obj = root;
        var xmlString = obj.XmlSerializeToString();

        //Make Object with Direct Deserialization
        var vehicles = xmlString.XmlDeserializeFromString<Vehicles>();

        //Make polymorphic object from generic vehicles with some "logic"
        var polymorphicVehicles = new List<Vehicle>();  // ******  THIS is the collection you requested!!!! *********

        // itterate all vehicles
        foreach (var item in vehicles.Items)
        {
            // use json serialization, because casting Parent to Child is not acceptable
            var jsonVehicle = JsonConvert.SerializeObject(item);

            // depending on the Name of the vehicle, create a corresponding object
            switch (item.Name)
            {
                case "Car":
                    var aCar = JsonConvert.DeserializeObject<Car>(jsonVehicle);
                    polymorphicVehicles.Add(aCar);
                    break;
                case "Truck":
                    var aTruck = JsonConvert.DeserializeObject<Truck>(jsonVehicle);
                    polymorphicVehicles.Add(aTruck);
                    break;
                case "Bike":
                    var aBike = JsonConvert.DeserializeObject<Bike>(jsonVehicle);
                    polymorphicVehicles.Add(aBike);
                    break;
                default:
                    break;
            }
        }


        // this is just to print it out!
        var jsonPolymorphicVehicles = JsonConvert.SerializeObject(polymorphicVehicles);

        Console.WriteLine("XML:");
        Console.WriteLine(xml);

        Console.WriteLine("");

        Console.WriteLine("Polymorphic to jason");
        Console.WriteLine(jsonPolymorphicVehicles);

        Console.WriteLine("");
        Console.WriteLine("Press key to exit!");
        Console.Read();
    }


}



public class Vehicle
{
    public string Name = "Vehicle";
}

public class Car : Vehicle
{
    public Car()
    {
        Name = "Car";
    }
}

public class Bike : Vehicle
{
    public Bike()
    {
        Name = "Bike";
    }
}

public class Truck : Vehicle
{
    public Truck()
    {
        Name = "Truck";
    }
}

public class Vehicles
{
    public List<Vehicle> Items { get; } = new List<Vehicle>();
}


public static class MyStaticClass
{
    public static T XmlDeserializeFromString<T>(this string objectData)
    {
        return (T)XmlDeserializeFromString(objectData, typeof(T));
    }

    public static string XmlSerializeToString(this object objectInstance)
    {
        var serializer = new XmlSerializer(objectInstance.GetType());
        var sb = new StringBuilder();

        using (TextWriter writer = new StringWriter(sb))
        {
            serializer.Serialize(writer, objectInstance);
        }

        return sb.ToString();
    }

    public static object XmlDeserializeFromString(this string objectData, Type type)
    {
        var serializer = new XmlSerializer(type);
        object result;

        using (TextReader reader = new StringReader(objectData))
        {
            result = serializer.Deserialize(reader);
        }

        return result;
    }
}

【讨论】:

    【解决方案2】:

    这是要序列化的代码和结果。 XML 不能将数组作为根元素。所以在这种情况下,有两个类是有意义的:Vehicles 和 Vehicle。请参阅下面的代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    using System.IO;
    
    namespace ConsoleApplication107
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.xml";
            static void Main(string[] args)
            {
                Vehicles vehicles = new Vehicles()
                {
                    vehicles = new List<Vehicle>() {
                        new Car() { make = "BMW"},
                        new Bike() { make = "Buffalo"},
                        new Truck() { make = "MAC"}
                    }
                };
    
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = true;
    
                XmlWriter writer = XmlWriter.Create(FILENAME, settings);
                XmlSerializer serializer = new XmlSerializer(typeof(Vehicles));
                serializer.Serialize(writer, vehicles);
    
            }
        }
    
        public class Vehicles
        {
            [XmlElement("Vehicle")]
            public List<Vehicle> vehicles { get; set; }
        }
        [XmlInclude(typeof(Car))]
        [XmlInclude(typeof(Bike))]
        [XmlInclude(typeof(Truck))]
        public class Vehicle
        {
            public string make { get; set; }
        }
        public class Car : Vehicle
        {
        }
        public class Bike : Vehicle
        {
        }
        public class Truck : Vehicle
        {
        }
    }
    

    这是结果:

    <?xml version="1.0" encoding="utf-8"?>
    <Vehicles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Vehicle xsi:type="Car">
        <make>BMW</make>
      </Vehicle>
      <Vehicle xsi:type="Bike">
        <make>Buffalo</make>
      </Vehicle>
      <Vehicle xsi:type="Truck">
        <make>MAC</make>
      </Vehicle>
    </Vehicles>
    

    【讨论】:

    • 仅供参考,它也可以通过元素命名而不是通过 xsi:type 来完成 - 但两者都是有效的(它实际上归结为:意味着是什么在具体实现中)
    • 这是将 obect 序列化为 xml 的程序,但我想将 xml 内容反序列化为对象列表。
    • 我刚刚发布了序列化数据,以便您获得 xml 文件的格式。您可以更改代码以轻松反序列化。如果 XML 文件与类结构不匹配,反序列化将失败,因此我始终建议先序列化,然后将序列化 xml 与实际输入的 xml 进行比较,以查看结构是否匹配。
    • @user8833423 以回应:它们是同一件事 - 通过将 XmlSerializer 序列化 显示为预期格式,这也意味着我们已将其配置为 反序列化同样的东西——你只需使用Deserialize(...)而不是Serialize(...)
    • @jdweng 似乎可以解决我的问题。谢谢大家的帮助。
    【解决方案3】:

    XmlSerializer 支持某些类型的继承建模,但是:它基于元素/属性,而不是实际值;我不知道有任何 API 会支持您想要从该数据中获得什么,因此您必须将它们反序列化为字符串,并将它们后处理为您想要的。

    可能的一个例子是:

    <Vehicles>
        <Car>...car things...</Car>
        <Bike>...bike things...</Bike>
        <Truck>...truck things...</Truck>
    </Vehicles>
    

    可以通过以下方式实现:

    using System;
    using System.Collections.Generic;
    using System.Xml.Serialization;
    
    [XmlInclude(typeof(Car))] // technically you only need XmlInclude
    [XmlInclude(typeof(Bike))] // if you're using xsi:type resolution
    [XmlInclude(typeof(Truck))] // but... it doesn't hurt us here
    public abstract class Vehicle { }
    
    public class Car : Vehicle { }
    public class Truck : Vehicle { }
    public class Bike : Vehicle { }
    
    [XmlRoot("Vehicles")]
    public class MyRoot
    {
        [XmlElement("Car", Type = typeof(Car))]
        [XmlElement("Truck", Type = typeof(Truck))]
        [XmlElement("Bike", Type = typeof(Bike))]
        public List<Vehicle> Items { get; } = new List<Vehicle>();
    }
    static class P
    {
        static void Main()
        {
            var root = new MyRoot
            {
                Items =
                {
                    new Car(),
                    new Bike(),
                    new Truck(),
                }
            };
            var ser = new XmlSerializer(typeof(MyRoot));
            var ns = new XmlSerializerNamespaces();
            ns.Add("", "");
            ser.Serialize(Console.Out, root, ns);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-20
      • 2013-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多