更改 XML 序列化中的元素类型
Changing type of element in XML Serialization
我在 XML 序列化方面遇到了很大的问题。我有两个 classes,都需要可序列化。在继承的 class 中,我想更改序列化行为,以便将字符串 属性 序列化为复杂类型。
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.
您在同一图表级别指定了两个具有相同名称的不同 XML 元素,这是不允许的。
我将提供一个解决方案,但它涉及连接 Cat class 序列化的类型和名称。如果不必序列化 NameAndType class,则继续:
在Animal上,将Name设置为virtual;
在 Cat 上,在 Name2 上设置 XmlIgnore。然后按照您喜欢的方式覆盖 Name 和 return Name2 的两个属性。
如果您真的需要按原样序列化 class,恐怕您将不得不使用不同的元素名称来序列化它。
编辑:示例代码:
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 { }
}
}
出现错误的原因是,在 XmlSerializer
code generation 期间,代码生成器不理解 Cat
上的两个潜在 NAME
元素将永远不会被同时序列化,所以抛出异常。
相反,您可以将 XmlAnyElementAttribute
应用到虚拟 属性 returning 一个 XElement
,然后手动创建和 return 一个合适的 XElement
层次结构中每个 class 的名称:
[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 序列化方面遇到了很大的问题。我有两个 classes,都需要可序列化。在继承的 class 中,我想更改序列化行为,以便将字符串 属性 序列化为复杂类型。
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.
您在同一图表级别指定了两个具有相同名称的不同 XML 元素,这是不允许的。
我将提供一个解决方案,但它涉及连接 Cat class 序列化的类型和名称。如果不必序列化 NameAndType class,则继续: 在Animal上,将Name设置为virtual; 在 Cat 上,在 Name2 上设置 XmlIgnore。然后按照您喜欢的方式覆盖 Name 和 return Name2 的两个属性。
如果您真的需要按原样序列化 class,恐怕您将不得不使用不同的元素名称来序列化它。
编辑:示例代码:
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 { }
}
}
出现错误的原因是,在 XmlSerializer
code generation 期间,代码生成器不理解 Cat
上的两个潜在 NAME
元素将永远不会被同时序列化,所以抛出异常。
相反,您可以将 XmlAnyElementAttribute
应用到虚拟 属性 returning 一个 XElement
,然后手动创建和 return 一个合适的 XElement
层次结构中每个 class 的名称:
[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>