Как десериализовать дочерний объект в список родительских (например, List) на основе значений xml-узлов?

#c# #deserialization

#c# #десериализация

Вопрос:

У меня есть один XML-файл под названием VehicleInfo. Я хочу десериализовать VehicleInfo в списке транспортных средств. Теперь у меня есть один базовый класс с именем Vehicle и три производных класса с именами Car, Bike, Truck. Как десериализовать конкретный объект vehicle на основе значения узла Vehicle в 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";
}
  

Класс Car :

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

Класс Bike :

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

Класс Truck :

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

Эта программа Vehicle — всего лишь пример,

Итак, как я могу десериализовать конкретный объект (такой как автомобиль, велосипед или грузовик) в списке транспортных средств в классе VehicleList на основе значения узла Vehicle.

Комментарии:

1. Создайте образец и сериализуйте, чтобы увидеть структуру перед десериализацией. Вам нужно использовать атрибут Include. Смотрите :

2. Привет, и добро пожаловать в SO! Чтобы получить справку, предоставьте также структуру ваших классов. Базовый и дочерний классы (по крайней мере, некоторые их базовые поля)

3. Что вы пробовали?

Ответ №1:

Вот код и результаты для сериализации. 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:temptest.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>
  

Комментарии:

1. К вашему сведению, это также можно сделать с помощью именования элементов, а не через xsi: type — но любой из них допустим (на самом деле все сводится к тому, что это означает в конкретной реализации)

2. Это программа для сериализации объекта в xml, но я хочу десериализовать содержимое xml в список объектов.

3. Я только что опубликовал сериализацию данных, чтобы вы получили формат XML-файла. Вы можете легко изменить код для десериализации. Десериализация завершится неудачей, если XML-файл не соответствует структурам классов, поэтому я всегда рекомендую сначала сериализовать, а затем сравнить serialize xml с вашим фактическим входным xml, чтобы увидеть, соответствует ли структура.

4.@user8833423 повторяю: это одно и то же — показывая, что что-то XmlSerializer сериализуется в заданном формате, это также означает, что мы настроили его на десериализацию того же самого чего-то — вы просто используете Deserialize(...) , а не Serialize(...)

5. @jdweng, похоже, сработал для моей проблемы. Спасибо вам всем за помощь.

Ответ №2:

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

Ответ №3:

Хорошо, это будет долго…

Вы можете поиграть с этим решением .net-fiddle-здесь

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

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

        return resu<
    }
}