更改XML序列化中的元素类型
本文关键字:元素 类型 XML 序列化 更改 | 更新日期: 2023-09-27 17:49:19
我在XML序列化方面遇到了很大的问题。我有两个类,都需要是可序列化的。在继承的类中,我想更改序列化行为,以便将字符串属性序列化为复杂类型。
public class Animal
{
[XmlElement(ElementName = "NAME")]
public string Name { get; set; }
public virtual bool ShouldSerializeName() { return true; }
}
public class Cat : Animal
{
public override bool ShouldSerializeName() { return false; }
[XmlElement(ElementName = "NAME")]
public NameAndType Name2 { get; set; }
}
public class NameAndType
{
public string Name { get; set; }
public string Type { get; set; }
}
...
var cat = new Cat {Name2 = new NameAndType {Name = "LittleCat"}};
new XmlSerializer(typeof(Cat)).Serialize(Console.Out, cat);
我尝试了不同的方法,但我没有找到一种方法来改变NAME
元素的序列化方式。在上面的例子中,我得到了错误消息:
The XML element 'NAME' from namespace '' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.
您得到错误的原因是,在XmlSerializer
代码生成期间,代码生成器不理解Cat
上的两个潜在的NAME
元素永远不会同时序列化,因此抛出异常。
相反,您可以将XmlAnyElementAttribute
应用于返回XElement
的虚拟属性,然后为层次结构中的每个类的名称手动创建并返回适当的XElement
:
[XmlInclude(typeof(Cat))]
public class Animal
{
[XmlIgnore]
public string Name { get; set; }
[XmlAnyElement]
public virtual XElement XmlName
{
get
{
return Name == null ? null : new XElement("NAME", Name);
}
set
{
Name = (value == null ? null : value.Value);
}
}
}
public class Cat : Animal
{
// Must be cached as per https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer%28v=vs.110%29.aspx
static XmlSerializer nameSerializer;
static Cat()
{
nameSerializer = new XmlSerializer(typeof(NameAndType), new XmlRootAttribute("NAME"));
}
[XmlIgnore]
public NameAndType Name2 { get; set; }
[XmlAnyElement]
public override XElement XmlName
{
get
{
return (Name2 == null ? null : XObjectExtensions.SerializeToXElement(Name2, nameSerializer, true));
}
set
{
Name2 = (value == null ? null : XObjectExtensions.Deserialize<NameAndType>(value, nameSerializer));
}
}
}
使用扩展方法
public static class XObjectExtensions
{
public static T Deserialize<T>(this XContainer element)
{
return element.Deserialize<T>(new XmlSerializer(typeof(T)));
}
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
{
using (var reader = element.CreateReader())
{
object result = serializer.Deserialize(reader);
if (result is T)
return (T)result;
}
return default(T);
}
public static XElement SerializeToXElement<T>(this T obj)
{
return obj.SerializeToXElement(new XmlSerializer(obj.GetType()), true);
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
(ns = new XmlSerializerNamespaces()).Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
serializer.Serialize(writer, obj, ns);
}
var element = doc.Root;
if (element != null)
element.Remove();
return element;
}
}
对于List<Animal>
,生成如下XML:
<ArrayOfAnimal xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Animal> <NAME>duck</NAME> </Animal> <Animal xsi:type="Cat"> <NAME> <Name>Smokey</Name> <Type>Siamese</Type> </NAME> </Animal> </ArrayOfAnimal>
您在同一图形级别上指定了两个具有相同名称的不同XML元素,这是不允许的。
我将提供一个解决方案,但它涉及到Cat类序列化的类型和名称的连接。如果不是必须序列化NameAndType类,那么继续:在Animal上,将Name设置为virtual;在Cat上,在Name2上设置XmlIgnore。然后重写Name,并按您喜欢的方式返回Name2的两个属性。如果你真的需要序列化这个类,那么恐怕你必须用一个不同的elementName。
编辑:示例代码:public class Animal
{
[XmlElement(ElementName = "NAME")]
public virtual string Name { get; set; }
public virtual bool ShouldSerializeName() { return true; }
}
public class Cat : Animal
{
public override bool ShouldSerializeName() { return false; }
[XmlIgnore]
public NameAndType Name2 { get; set; }
[XmlElement(ElementName = "NAME")]
public override string Name
{
get
{
return String.Format("{0} [Type:{1}]", Name2.Name, Name2.Type);
}
set { }
}
}