如何根据元素的值将 xml 反序列化为派生的 类?
How to deserialize xml to derived classes base on element's value?
例如我有一个 xml:
<MyFruit>
<Fruit>
<Name>Apple</Name>
<Size>Big</Size>
</Fruit>
<Fruit>
<Name>Orange</Name>
<Price>10.00</Price>
</Fruit>
</MyFruit>
您可能会注意到 fruit
节点包含不同的元素,这是我的伤害:(
然后我定义了以下 classes 以保存反序列化的对象:
public class MyFruit
{
public List<Fruit> Fruits { get; set; }
}
public abstract class Fruit
{
public string Name { get; set; }
}
public class Apple : Fruit
{
public string Size { get; set; }
}
public class Orange : Fruit
{
public float Price { get; set; }
}
没用。
我也试过:
- 将
[XmlInclude(typeof (Apple))]
和 [XmlInclude(typeof (Orange))]
属性添加到 fruit
基础 class 以指定具体派生 classes
- 将
[XmlElement(typeof (Apple))]
和 [XmlElement(typeof (Orange))
属性添加到 MyFruit
class 的 Fruits
属性
两者都不行。
所以我想知道有没有一种方法可以根据元素的值控制反序列化过程(如果名称是Apple
,反序列化为Apple
class,Orange
到 Orange
class...),或者也许有一些更好的方法?
更新
我写了一个反序列化的扩展方法xml:
public static T Deserialize<T>(this string xml)
{
if (string.IsNullOrEmpty(xml))
{
return default(T);
}
try
{
var xmlserializer = new XmlSerializer(typeof(T));
var stringReader = new StringReader(xml);
using (var reader = XmlReader.Create(stringReader))
{
return (T) xmlserializer.Deserialize(reader);
}
}
catch (Exception ex)
{
throw new Exception("反序列化发生错误", ex);
}
}
一种方法是简单地将输入 xml 通过 XslCompiledTransform
class 转换为可以轻松反序列化为所需对象结构的格式。以下示例演示了该概念:
// XML Deserialization helper.
class XmlSerializationHelper
{
// Transform the input xml to the desired format needed for de-serialization.
private static string TransformXml(string xmlString)
{
// XSL transformation script.
string xsl = @"<xsl:stylesheet xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"" version=""1.0"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<xsl:template match=""MyFruit"">
<xsl:element name=""{local-name()}"">
<Fruits>
<xsl:for-each select=""Fruit"">
<xsl:element name=""Fruit"">
<xsl:attribute name=""xsi:type""><xsl:value-of select=""Name""/></xsl:attribute>
<xsl:copy-of select=""./node()""/>
</xsl:element>
</xsl:for-each>
</Fruits>
</xsl:element>
</xsl:template>
</xsl:stylesheet>";
// Load input xml as XmlDocument
XmlDocument sourceXml = new XmlDocument();
sourceXml.LoadXml(xmlString);
// Create XSL transformation.
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(new XmlTextReader(new StringReader(xsl)));
// Apply transformation to input xml and write result out to target xml doc.
XmlDocument targetXml = new XmlDocument(sourceXml.CreateNavigator().NameTable);
using (XmlWriter writer = targetXml.CreateNavigator().AppendChild())
{
transform.Transform(sourceXml, writer);
}
// Return transformed xml string.
return targetXml.InnerXml;
}
public static T DeSerialize<T>(string inputXml)
{
T instance = default(T);
if (string.IsNullOrEmpty(inputXml))
return instance;
try
{
string xml = TransformXml(inputXml); // Transform the input xml to the desired xml format needed to de-serialize objects.
string attributeXml = string.Empty;
using (StringReader reader = new StringReader(xml))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (XmlReader xmlReader = new XmlTextReader(reader))
{
instance = (T)serializer.Deserialize(xmlReader);
xmlReader.Close();
}
reader.Close();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return instance;
}
}
助手 class 现在可以按如下方式使用:
string inputXml = @"<MyFruit>
<Fruit>
<Name>Apple</Name>
<Size>Big</Size>
</Fruit>
<Fruit>
<Name>Orange</Name>
<Price>10.00</Price>
</Fruit>
</MyFruit>";
MyFruit fruits = XmlSerializationHelper.DeSerialize<MyFruit>(inputXml);
您可以使用循环和 XmlParser。打开 'Name' 属性 以解析 class 特定标签。示例:
Fruit fruit;
List<Fruit> fruits;
while(true)
{
// ...
xmlReader.Read();
xmlReader.ReadStartElement("Fruit");
xmlReader.ReadStartElement("Name");
name = xmlReader.ReadString();
xmlReader.ReadEndElement();
switch(name)
{
case "apple":
fruit = new Apple();
try
{
xmlReader.ReadStartElement("weight");
(fruit as Apple).weight = Integer.Parse(xmlReader.ReadString());
xmlReader.ReadEndElement();
}catch(){}
//.....
break;
case "orange":
fruit = new Orange;
try
{
xmlReader.ReadStartElement("color");
(fruit as Orange).color = xmlReader.ReadString();
xmlReader.ReadEndElement();
}catch(){}
//.....
break;
}
xmlReader.ReadEndElement();
// ...
}
这个会起作用:
[XmlRoot("MyFruit")]
public class MyFruit : List<Fruit>
{
}
public class Fruit
{
public string Name { get; set; }
public string Size { get; set; }
public float Price { get; set; }
}
使用来自@WAKU 的反序列化
例如我有一个 xml:
<MyFruit>
<Fruit>
<Name>Apple</Name>
<Size>Big</Size>
</Fruit>
<Fruit>
<Name>Orange</Name>
<Price>10.00</Price>
</Fruit>
</MyFruit>
您可能会注意到 fruit
节点包含不同的元素,这是我的伤害:(
然后我定义了以下 classes 以保存反序列化的对象:
public class MyFruit
{
public List<Fruit> Fruits { get; set; }
}
public abstract class Fruit
{
public string Name { get; set; }
}
public class Apple : Fruit
{
public string Size { get; set; }
}
public class Orange : Fruit
{
public float Price { get; set; }
}
没用。
我也试过:
- 将
[XmlInclude(typeof (Apple))]
和[XmlInclude(typeof (Orange))]
属性添加到fruit
基础 class 以指定具体派生 classes - 将
[XmlElement(typeof (Apple))]
和[XmlElement(typeof (Orange))
属性添加到MyFruit
class 的
Fruits
属性
两者都不行。
所以我想知道有没有一种方法可以根据元素的值控制反序列化过程(如果名称是Apple
,反序列化为Apple
class,Orange
到 Orange
class...),或者也许有一些更好的方法?
更新
我写了一个反序列化的扩展方法xml:
public static T Deserialize<T>(this string xml)
{
if (string.IsNullOrEmpty(xml))
{
return default(T);
}
try
{
var xmlserializer = new XmlSerializer(typeof(T));
var stringReader = new StringReader(xml);
using (var reader = XmlReader.Create(stringReader))
{
return (T) xmlserializer.Deserialize(reader);
}
}
catch (Exception ex)
{
throw new Exception("反序列化发生错误", ex);
}
}
一种方法是简单地将输入 xml 通过 XslCompiledTransform
class 转换为可以轻松反序列化为所需对象结构的格式。以下示例演示了该概念:
// XML Deserialization helper.
class XmlSerializationHelper
{
// Transform the input xml to the desired format needed for de-serialization.
private static string TransformXml(string xmlString)
{
// XSL transformation script.
string xsl = @"<xsl:stylesheet xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"" version=""1.0"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<xsl:template match=""MyFruit"">
<xsl:element name=""{local-name()}"">
<Fruits>
<xsl:for-each select=""Fruit"">
<xsl:element name=""Fruit"">
<xsl:attribute name=""xsi:type""><xsl:value-of select=""Name""/></xsl:attribute>
<xsl:copy-of select=""./node()""/>
</xsl:element>
</xsl:for-each>
</Fruits>
</xsl:element>
</xsl:template>
</xsl:stylesheet>";
// Load input xml as XmlDocument
XmlDocument sourceXml = new XmlDocument();
sourceXml.LoadXml(xmlString);
// Create XSL transformation.
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(new XmlTextReader(new StringReader(xsl)));
// Apply transformation to input xml and write result out to target xml doc.
XmlDocument targetXml = new XmlDocument(sourceXml.CreateNavigator().NameTable);
using (XmlWriter writer = targetXml.CreateNavigator().AppendChild())
{
transform.Transform(sourceXml, writer);
}
// Return transformed xml string.
return targetXml.InnerXml;
}
public static T DeSerialize<T>(string inputXml)
{
T instance = default(T);
if (string.IsNullOrEmpty(inputXml))
return instance;
try
{
string xml = TransformXml(inputXml); // Transform the input xml to the desired xml format needed to de-serialize objects.
string attributeXml = string.Empty;
using (StringReader reader = new StringReader(xml))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (XmlReader xmlReader = new XmlTextReader(reader))
{
instance = (T)serializer.Deserialize(xmlReader);
xmlReader.Close();
}
reader.Close();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return instance;
}
}
助手 class 现在可以按如下方式使用:
string inputXml = @"<MyFruit>
<Fruit>
<Name>Apple</Name>
<Size>Big</Size>
</Fruit>
<Fruit>
<Name>Orange</Name>
<Price>10.00</Price>
</Fruit>
</MyFruit>";
MyFruit fruits = XmlSerializationHelper.DeSerialize<MyFruit>(inputXml);
您可以使用循环和 XmlParser。打开 'Name' 属性 以解析 class 特定标签。示例:
Fruit fruit;
List<Fruit> fruits;
while(true)
{
// ...
xmlReader.Read();
xmlReader.ReadStartElement("Fruit");
xmlReader.ReadStartElement("Name");
name = xmlReader.ReadString();
xmlReader.ReadEndElement();
switch(name)
{
case "apple":
fruit = new Apple();
try
{
xmlReader.ReadStartElement("weight");
(fruit as Apple).weight = Integer.Parse(xmlReader.ReadString());
xmlReader.ReadEndElement();
}catch(){}
//.....
break;
case "orange":
fruit = new Orange;
try
{
xmlReader.ReadStartElement("color");
(fruit as Orange).color = xmlReader.ReadString();
xmlReader.ReadEndElement();
}catch(){}
//.....
break;
}
xmlReader.ReadEndElement();
// ...
}
这个会起作用:
[XmlRoot("MyFruit")]
public class MyFruit : List<Fruit>
{
}
public class Fruit
{
public string Name { get; set; }
public string Size { get; set; }
public float Price { get; set; }
}
使用来自@WAKU 的反序列化