Ela*_*lad 79 .net c# serialization
我想XML序列化一个对象,该对象具有(以及其他)IModelObject类型的属性(这是一个接口).
public class Example
{
public IModelObject Model { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
当我尝试序列化此类的对象时,我收到以下错误:
"无法序列化示例类型的Example.Model,因为它是一个接口."
我知道问题是接口无法序列化.但是,具体的Model对象类型在运行时才会被识别.
用抽象或具体类型替换IModelObject接口并使用XMLInclude继承是可能的,但似乎是一个丑陋的解决方法.
有什么建议?
Shu*_*oUk 112
这只是声明性序列化的固有限制,其中类型信息未嵌入在输出中.
试图转换<Flibble Foo="10" />回来
public class Flibble { public object Foo { get; set; } }
Run Code Online (Sandbox Code Playgroud)
串行器如何知道它应该是int,string,double(或其他)...
为了使这项工作你有几个选择,但如果你真的不知道直到运行时,最简单的方法是使用XmlAttributeOverrides.
遗憾的是,这只适用于基类,而不适用于接口.您可以做的最好的事情是忽略不足以满足您需求的房产.
如果你真的必须使用接口,你有三个真正的选择:
丑陋,令人不快的锅炉板和多次重复,但班上的大多数消费者将不必处理这个问题:
[XmlIgnore()]
public object Foo { get; set; }
[XmlElement("Foo")]
[EditorVisibile(EditorVisibility.Advanced)]
public string FooSerialized
{
get { /* code here to convert any type in Foo to string */ }
set { /* code to parse out serialized value and make Foo an instance of the proper type*/ }
}
Run Code Online (Sandbox Code Playgroud)
这很可能成为维护的噩梦......
类似于第一个选项,你可以完全控制事物
重复劳动的问题与第一个问题类似.
public sealed class XmlAnything<T> : IXmlSerializable
{
public XmlAnything() {}
public XmlAnything(T t) { this.Value = t;}
public T Value {get; set;}
public void WriteXml (XmlWriter writer)
{
if (Value == null)
{
writer.WriteAttributeString("type", "null");
return;
}
Type type = this.Value.GetType();
XmlSerializer serializer = new XmlSerializer(type);
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
serializer.Serialize(writer, this.Value);
}
public void ReadXml(XmlReader reader)
{
if(!reader.HasAttributes)
throw new FormatException("expected a type attribute!");
string type = reader.GetAttribute("type");
reader.Read(); // consume the value
if (type == "null")
return;// leave T at default value
XmlSerializer serializer = new XmlSerializer(Type.GetType(type));
this.Value = (T)serializer.Deserialize(reader);
reader.ReadEndElement();
}
public XmlSchema GetSchema() { return(null); }
}
Run Code Online (Sandbox Code Playgroud)
使用它会涉及类似(在项目P中):
public namespace P
{
public interface IFoo {}
public class RealFoo : IFoo { public int X; }
public class OtherFoo : IFoo { public double X; }
public class Flibble
{
public XmlAnything<IFoo> Foo;
}
public static void Main(string[] args)
{
var x = new Flibble();
x.Foo = new XmlAnything<IFoo>(new RealFoo());
var s = new XmlSerializer(typeof(Flibble));
var sw = new StringWriter();
s.Serialize(sw, x);
Console.WriteLine(sw);
}
}
Run Code Online (Sandbox Code Playgroud)
这给你:
<?xml version="1.0" encoding="utf-16"?>
<MainClass
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Foo type="P.RealFoo, P, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<RealFoo>
<X>0</X>
</RealFoo>
</Foo>
</MainClass>
Run Code Online (Sandbox Code Playgroud)
对于班级用户来说,这显然更加麻烦,尽管避免了很多锅炉板.
一个幸福的媒介可能是将XmlAnything想法合并到第一种技术的"支持"属性中.通过这种方式,大部分繁重的工作都是为你完成的,但是除了内省的混乱之外,班级的消费者不会受到任何影响.
Des*_*tar 40
解决方法是使用DataContractSerializer进行反射.您甚至不必使用[DataContract]或[DataMember]标记您的类.它将序列化任何对象,无论它是否具有接口类型属性(包括字典)到xml中.这是一个简单的扩展方法,它将任何对象序列化为XML,即使它有接口(注意你可以调整它以便递归运行).
public static XElement ToXML(this object o)
{
Type t = o.GetType();
Type[] extraTypes = t.GetProperties()
.Where(p => p.PropertyType.IsInterface)
.Select(p => p.GetValue(o, null).GetType())
.ToArray();
DataContractSerializer serializer = new DataContractSerializer(t, extraTypes);
StringWriter sw = new StringWriter();
XmlTextWriter xw = new XmlTextWriter(sw);
serializer.WriteObject(xw, o);
return XElement.Parse(sw.ToString());
}
Run Code Online (Sandbox Code Playgroud)
LINQ表达式的作用是枚举每个属性,返回作为接口的每个属性,获取该属性的值(底层对象),获取该具体对象的类型将其放入数组中,并将其添加到序列化程序中已知类型列表.
现在,序列化器知道它序列化的类型如何,它可以完成它的工作.
您可以使用ExtendedXmlSerializer.此序列化程序支持接口属性的序列化,没有任何技巧.
var serializer = new ConfigurationContainer().UseOptimizedNamespaces().Create();
var obj = new Example
{
Model = new Model { Name = "name" }
};
var xml = serializer.Serialize(obj);
Run Code Online (Sandbox Code Playgroud)
你的xml看起来像:
<?xml version="1.0" encoding="utf-8"?>
<Example xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Simple;assembly=ExtendedXmlSerializer.Samples">
<Model exs:type="Model">
<Name>name</Name>
</Model>
</Example>
Run Code Online (Sandbox Code Playgroud)
ExtendedXmlSerializer支持.net 4.5和.net Core.
如果您事先知道您的接口实现者,那么您可以使用相当简单的hack来使您的接口类型序列化而无需编写任何解析代码:
public interface IInterface {}
public class KnownImplementor01 : IInterface {}
public class KnownImplementor02 : IInterface {}
public class KnownImplementor03 : IInterface {}
public class ToSerialize {
[XmlIgnore]
public IInterface InterfaceProperty { get; set; }
[XmlArray("interface")]
[XmlArrayItem("ofTypeKnownImplementor01", typeof(KnownImplementor01))]
[XmlArrayItem("ofTypeKnownImplementor02", typeof(KnownImplementor02))]
[XmlArrayItem("ofTypeKnownImplementor03", typeof(KnownImplementor03))]
public object[] InterfacePropertySerialization {
get { return new[] { InterfaceProperty }; ; }
set { InterfaceProperty = (IInterface)value.Single(); }
}
}
Run Code Online (Sandbox Code Playgroud)
生成的xml应该看起来像是行
<interface><ofTypeKnownImplementor01><!-- etc... -->
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
74056 次 |
| 最近记录: |