我有一个自定义Fraction类,我在整个项目中使用它。它很简单,它由一个构造函数组成,接受两个int并存储它们。我想使用DataContractSerializer来序列化我的项目中使用的对象,其中一些包括Fractions作为字段。理想情况下,我希望能够像这样序列化这样的对象:
<Object>
...
<Frac>1/2</Frac> // "1/2" would get converted back into a Fraction on deserialization.
...
</Object>
与此相反:
<Object>
...
<Frac>
<Numerator>1</Numerator>
<Denominator>2</Denominator>
</Frac>
...
</Object>
有没有办法使用DataContracts做到这一点?
我想这样做是因为我计划将XML文件设置为用户可编辑的(我将它们用作音乐游戏的输入,并且它们本质上充当记事本),并希望将符号保持为尽可能简化最终用户,因此他们不需要处理尽可能多的文本。
编辑:我还应该注意,我目前将我的Fraction类视为不可变(所有字段都是readonly
),因此能够更改现有Fraction的状态不会有可能。但是,返回一个新的Fraction对象就可以了。
答案 0 :(得分:6)
如果您添加一个表示Frac元素的属性并将DataMember属性应用于它而不是其他属性,您将获得我想要的内容:
[DataContract]
public class MyObject {
Int32 _Numerator;
Int32 _Denominator;
public MyObject(Int32 numerator, Int32 denominator) {
_Numerator = numerator;
_Denominator = denominator;
}
public Int32 Numerator {
get { return _Numerator; }
set { _Numerator = value; }
}
public Int32 Denominator {
get { return _Denominator; }
set { _Denominator = value; }
}
[DataMember(Name="Frac")]
public String Fraction {
get { return _Numerator + "/" + _Denominator; }
set {
String[] parts = value.Split(new char[] { '/' });
_Numerator = Int32.Parse(parts[0]);
_Denominator = Int32.Parse(parts[1]);
}
}
}
答案 1 :(得分:5)
DataContractSerializer
会使用自定义IXmlSerializable
代替DataContractAttribute
。这将允许您以任何方式自定义XML格式...但您必须为您的类手动编写序列化和反序列化过程。
public class Fraction: IXmlSerializable
{
private Fraction()
{
}
public Fraction(int numerator, int denominator)
{
this.Numerator = numerator;
this.Denominator = denominator;
}
public int Numerator { get; private set; }
public int Denominator { get; private set; }
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
var content = reader.ReadInnerXml();
var parts = content.Split('/');
Numerator = int.Parse(parts[0]);
Denominator = int.Parse(parts[1]);
}
public void WriteXml(XmlWriter writer)
{
writer.WriteRaw(this.ToString());
}
public override string ToString()
{
return string.Format("{0}/{1}", Numerator, Denominator);
}
}
[DataContract(Name = "Object", Namespace="")]
public class MyObject
{
[DataMember]
public Fraction Frac { get; set; }
}
class Program
{
static void Main(string[] args)
{
var myobject = new MyObject
{
Frac = new Fraction(1, 2)
};
var dcs = new DataContractSerializer(typeof(MyObject));
string xml = null;
using (var ms = new MemoryStream())
{
dcs.WriteObject(ms, myobject);
xml = Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine(xml);
// <Object><Frac>1/2</Frac></Object>
}
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
ms.Position = 0;
var obj = dcs.ReadObject(ms) as MyObject;
Console.WriteLine(obj.Frac);
// 1/2
}
}
}
答案 2 :(得分:3)
This MSDN article描述 IDataContractSurrogate 接口:
提供将一种类型替换为另一种类型所需的方法 DataContractSerializer在序列化,反序列化和 导出和导入XML模式文档。
虽然为时已晚,仍然可以帮助某人。实际上,允许为任何类更改XML。
答案 3 :(得分:1)
你可以使用DataContractSerializer来做到这一点,虽然这种方式让我觉得很烦。您可以利用数据成员可以是私有变量的事实,并使用私有字符串作为序列化成员。数据协定序列化程序还将在进程中某些标记有[On(De)Serializ(ed | ing)]属性的点处执行方法 - 在这些属性中,您可以控制int字段如何映射到字符串,以及反之亦然。缺点是您失去了类上DataContractSerializer的自动序列化魔力,现在有更多逻辑需要维护。
无论如何,这就是我要做的事情:
[DataContract]
public class Fraction
{
[DataMember(Name = "Frac")]
private string serialized;
public int Numerator { get; private set; }
public int Denominator { get; private set; }
[OnSerializing]
public void OnSerializing(StreamingContext context)
{
// This gets called just before the DataContractSerializer begins.
serialized = Numerator.ToString() + "/" + Denominator.ToString();
}
[OnDeserialized]
public void OnDeserialized(StreamingContext context)
{
// This gets called after the DataContractSerializer finishes its work
var nums = serialized.Split("/");
Numerator = int.Parse(nums[0]);
Denominator = int.Parse(nums[1]);
}
}
答案 4 :(得分:0)
您必须切换回XMLSerializer才能执行此操作。在能够自定义输出方面,DataContractSerializer的限制性更强。