(De)сериализация различных имен корневых элементов с использованием одного класса
у меня есть несколько XML-файлов с разными корневыми элементами, но тот же тип дочерних элементов, и я хотел бы иметь возможность создавать один класс для хранения разных корневых элементов и другой для хранения каждого дочернего элемента. Вот два примера того, как выглядят XML-файлы.
1:<?xml version="1.0" encoding="utf-8" ?>
<Sandra>
<Address>
<Street></Street>
<Number></Number>
</Address>
</Sandra>
2:
<?xml version="1.0" encoding="utf-8" ?>
<John>
<Address>
<Street></Street>
<Number></Number>
</Address>
</John>
Я хочу иметь возможность как сериализовать, так и десериализовать это, используя только 2 класса, например:
[Serializable]
[XmlRoot]
public class Person
{
[XmlElement("Address")]
public List<Address> Adresses { get; set; }
}
[Serializable]
public class Address
{
public string Street { get; set; }
public string Number { get; set; }
}
я попытался чтобы прочитать их, используя:
var ser = new XmlSerializer(typeof(Person));
var reader = XmlReader.Create("person1.xml");
var person = (Person)ser.Deserialize(reader);
но я "There is an error in XML document (2, 2)."
, потому что десериализатор ожидал <"Person">
корневой элемент, а не <"John">
или <"Paul">
. Он отлично работает, если я изменяю [XmlRoot]
to [XmlRoot("John")]
но это именно то, что я пытаюсь избежать здесь.
кроме того, я должен иметь возможность сериализовать его обратно, используя ту же странную структуру XML, поэтому мне нужно сохранить имя корневого элемента внутри класса Person.
Я знаю, что мог бы просто создать свой собственный (de)сериализатор, но я хотел бы знать, можно ли его достичь с помощью существующих методов.
Edit 1: (откат).
Edit 2: откатил изменения "Edit 1", так как я нашел более простой способ достичь того, что мне нужно. См. мой ответ ниже.
4 ответов
нашли аккуратный и быстрый способ решить мою проблему! Мне просто пришлось использовать XmlRootAttribute при создании экземпляра XmlSerializer. Таким образом я могу установить имя корневого элемента во время выполнения.
var personsName = "Sandra";
var ser = new XmlSerializer(typeof(Person),
new XmlRootAttribute { ElementName = personsName });
var reader = XmlReader.Create("person1.xml");
var person = (Person)ser.Deserialize(reader);
конечно, он также работает так же, если я хочу сериализовать его.
использование имен людей в качестве имен элементов XML кажется немного странным. Что представляет собой каждый элемент, a John
или Person
?
было бы лучше, если вы можете контролировать форму этих XML-файлов, чтобы представить их так:
<?xml version="1.0" encoding="utf-8" ?>
<Person name="Sandra">
<Address>
<Street></Street>
<Number></Number>
</Address>
</Person>
и тогда у вас будет простой способ сохранить имя в свойстве, сопоставленном как атрибут XML:
[Serializable]
[XmlRoot]
public class Person
{
[XmlElement("Address")]
public List<Address> Adresses { get; set; }
[XmlAttribute("name")]
public string Name { get; set;}
}
реализуйте IXmlSerializable в своем классе и выполните сериализацию так, как вы хотите в функциях :
ReadXml(System.Xml.XmlReader reader)
и WriteXml(System.Xml.XmlWriter writer)
пример :
[Serializable]
public class Threshold : IXmlSerializable
{
public int Type {get;set;}
public object Value {get;set;}
public string Name {get;set;}
public void ReadXml(System.Xml.XmlReader reader)
{
XElement thresholdXML = XElement.Load(reader);
if (!thresholdXML.HasElements || thresholdXML.IsEmpty)
return;
Type = (ThresholdType)int.Parse(thresholdXML.Element("Type").Value);
Value = Type.Equals(ThresholdType.Complex) ? thresholdXML.Element("Value").Value : (object)Decimal.Parse(thresholdXML.Element("Value").Value);
Name = thresholdXML.Element("Name").Value;
}
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void WriteXml(System.Xml.XmlWriter writer)
{
XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces();
xmlnsEmpty.Add("", "");
writer.WriteElementString("Type", ((int)Type).ToString("D"));
writer.WriteElementString("Value", Value.ToString());
writer.WriteElementString("Name", Name);
}
}
используйте корневой элемент как ArrayOfClassName, а затем попробуйте.
<ArrayOfAlarmSummary>
<AlarmSummary>
<ClientId>1</ClientId>
<StationId>2</StationId>
<StationName>Station-2</StationName>
<DateTime>13/09/15</DateTime>
<TagName>AI2_2</TagName>
<Description>TR DC Current</Description>
<Units>Amps</Units>
<NormalOperation>10 to 100</NormalOperation>
<AlarmValue>132.48</AlarmValue>
<AlarmDescription>
</AlarmDescription>
</AlarmSummary>
<AlarmSummary>
<ClientId>1</ClientId>
<StationId>2</StationId>
<StationName>Station-2</StationName>
<DateTime>13/09/15</DateTime>
<TagName>AI2_2</TagName>
<Description>TR AC Current</Description>
<Units>Amps</Units>
<NormalOperation>10 to 100</NormalOperation>
<AlarmValue>132.48</AlarmValue>
<AlarmDescription>
</AlarmDescription>
</AlarmSummary>
</ArrayOfAlarmSummary>
вот класс deserialize, используя который вы можете десериализовать свой Xml-файл.
public class SerializeDeserialize<T>
{
StringBuilder sbData;
StringWriter swWriter;
XmlDocument xDoc;
XmlNodeReader xNodeReader;
XmlSerializer xmlSerializer;
public SerializeDeserialize()
{
sbData = new StringBuilder();
}
public string SerializeData(T data)
{
XmlSerializer employeeSerializer = new XmlSerializer(typeof(T));
swWriter = new StringWriter(sbData);
employeeSerializer.Serialize(swWriter, data);
return sbData.ToString();
}
public T DeserializeData(string dataXML)
{
xDoc = new XmlDocument();
xDoc.LoadXml(dataXML);
xNodeReader = new XmlNodeReader(xDoc.DocumentElement);
xmlSerializer = new XmlSerializer(typeof(T));
var employeeData = xmlSerializer.Deserialize(xNodeReader);
T deserializedEmployee = (T)employeeData;
return deserializedEmployee;
}
}
называть класс
var appDomain = System.IO.Directory.GetCurrentDirectory() + "\AlarmFiles";
var strPath = Path.Combine(appDomain,
"Alarm_" + DateTime.Now.ToString("ddMMyyyy") + Constants.FileType.XmlFile);
var fi = new FileInfo(strPath);
if (fi.Exists)
{
try
{
var xmlString = System.IO.File.ReadAllText(strPath);
var serializeAlarmSummary =
new SerializeDeserialize<List<AlarmSummary>>();
return serializeAlarmSummary.DeserializeData(xmlString);
}
catch (Exception ex)
{
throw ex;
}
}