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