C# 在通过DataContract序列化时,是否有任何方法覆盖自定义类型转换为的字符串值?

C# 在通过DataContract序列化时,是否有任何方法覆盖自定义类型转换为的字符串值?,c#,serialization,xml-serialization,datacontract,C#,Serialization,Xml Serialization,Datacontract,在我的音乐/节奏游戏中,我使用序列化来保存用户创建的simfile(想想音乐标签或记事本)。那里没有太惊天动地的东西。但是,我使用DataContract来执行序列化,因为: 1) 我还需要序列化私有字段和受保护字段。我不在乎它们是否可见,主要是因为… 2) 我希望用户能够在他/她最喜欢的文本编辑器中编辑序列化文件,并能够将这些文件加载到游戏中(想想,这些文件将用于表示游戏中的音符) 我想序列化的自定义数据类型之一是我创建的一个分数类: using System.Runtime.Serializ

在我的音乐/节奏游戏中,我使用序列化来保存用户创建的simfile(想想音乐标签或记事本)。那里没有太惊天动地的东西。但是,我使用DataContract来执行序列化,因为:

1) 我还需要序列化私有字段和受保护字段。我不在乎它们是否可见,主要是因为…
2) 我希望用户能够在他/她最喜欢的文本编辑器中编辑序列化文件,并能够将这些文件加载到游戏中(想想,这些文件将用于表示游戏中的音符)

我想序列化的自定义数据类型之一是我创建的一个分数类:

using System.Runtime.Serialization;

namespace Fractions {
    [DataContract(Namespace="")] // Don't need the namespaces.
    public sealed class Fraction {
        // NOTE THAT THESE ARE "READONLY"!
        [DataMember(Name="Num", Order=1)] private readonly long numer;
        [DataMember(Name="Den", Order=2)] private readonly long denom;

        // ...LOTS OF STUFF...

        public static Fraction FromString(string str) {
            // Try and parse string and create a Fraction from it, and return it.
            // This is static because I'm returning a new created Fraction.
        }

        public override ToString() {
            return numer.ToString() + "/" + denom.ToString();
        }
    }
}
通过测试,它可以正常工作,序列化为以下表单的XML片段:

<Fraction>
   <Num>(INT64 VALUE AS STRING)</Num>
   <Den>(INT64 VALUE AS STRING)</Den>
</Fraction>

(INT64值作为字符串)
(INT64值作为字符串)
现在,我可以很高兴,继续我快乐的编码方式但我很挑剔。

我的最终用户可能不太熟悉XML,而且我的游戏中有很多更复杂的数据类型,其中包含很多分数,因此我更希望能够在XML simfile中表示分数(更简洁):

(分子)/(分母)
但是,我不知道如何在不中断自动(反)序列化的情况下实现这一点。我查看了
IXmlSerializable
接口,但由于我的数据类型需要可变才能工作,我被关闭了(ReadXml()不返回新的分数对象,但似乎是flash实例化了一个分数对象,并且您必须手动填充值,这由于
只读
而无法工作)。出于同样的原因,使用onserialize和OnDeserialized属性也不起作用。我真的希望分数类保持不变

我猜在序列化为XML时,有一个标准过程可以将原语转换为字符串或从字符串转换为原语。例如,任何数字原语都必须在序列化/反序列化时转换为字符串或从字符串转换为字符串。有没有办法将这种自动字符串从/到转换添加到我的分数类型?如果可能的话,我可以想象序列化过程是这样的:

1) 序列化此包含分数字段的复杂数据类型。
2) 开始序列化[DataMember]字段。
3) 嘿,这个字段是一个分数对象。分数能够完整地表示为字符串。直接将该字符串写入XML,而不是深入分数对象并写出其所有字段。

反序列化将以相反的方式工作:

1) 反序列化此数据,我们需要的是某某数据类型。
2) 开始反序列化字段。
3) 哦,看,一个分数对象,我可以继续读取内容字符串,将该字符串转换为一个新的分数对象,然后返回新创建的分数。

我能做些什么来实现这一点吗?谢谢


编辑:数据契约代理似乎是一条可行之路,但就我而言,我似乎无法理解它们,也无法让它们在我的游戏中发挥作用。或者更确切地说,它们在我的序列化元素中添加了一些讨厌的自动名称空间和ID字段。

我想您可能可以使用

但更简单的方法是在类型中有一个私有字符串成员fractionString,它将表示类型的字符串表示形式。您必须仅在对象构造期间初始化它(因为您的类型是不可变的)。现在,您可以从序列化中跳过num和den,并将fractionString字段标记为DataMember。这种方法的缺点是额外的空间消耗

public sealed class Fraction {

    private readonly long numer;
    private readonly long denom;

    [DataMember(Name="Fraction")
    private string fractionString;

编辑:没关系,只要重新阅读你想要的内容,就知道上面的内容行不通了

我也遇到了类似的问题,不过我使用的是类型安全枚举模式。在每种情况下,当您编写DataContract时,您都在包含非简单数据类型C的元素中指定,C决定将该类变成一个元素,然后在该类中查找该数据契约

不幸的是,这不是我们两人想要的

我的解决方案分为两部分:
1) 在我们想要包含的复杂类中(在您的例子中是分数),提供了将对象序列化和反序列化为字符串的方法。(我使用了cast操作符,因为这对我来说意义最大):


虽然我承认这不是一个理想的解决方案,需要在using类中存在额外的代码,但它确实允许DataContract格式中复杂类的任意字符串表示,而不需要额外的层。

是的,遗憾的是,简单的方法在我的情况下不起作用。我会尝试尝试尝试一下数据契约代理,但它看起来像是一个相当大的跳水;到目前为止,我一点也不了解。@Mark,有关DataContract代理,请参阅MSDN中的示例:以及本文:。然而,我不认为你的问题也能解决。数据协定序列化不允许完整的模式控制—其主要目的是具有易于采用(和反序列化)的简单序列化格式。对于完整的模式控制,XMLSerializer是一个不错的选择。对于设置只读字段,可以使用反射。
public sealed class Fraction {

    private readonly long numer;
    private readonly long denom;

    [DataMember(Name="Fraction")
    private string fractionString;
class Complex
{
    ...
    // NOTE: that this can be implicit since every Complex generates a valid string        
    public static implicit operator string(Complex value)
    {
        return <... code to generate a string from the Complex Type...>;
    } 
    // NOTE: this must be explicit since it can throw an exception because not all
    // strings are valid Complex types
    public static explicit operator Complex(string value)
    {
        return <... code to validate and create a Complex object from a string ...>;
    }
    ...
 }
[DataContract]
class User
{
    ...
    [DataMember]
    public string MyComplex
    {
         get { return m_MyComplex; }
         set { m_myComplex = (Complex)value; }
    }
    // NOTE that this member is _not_ part of the DataContract
    Complex m_myComplex;
    ...
}