实现复合数据类型的自定义XML序列化/反序列化?

时间:2014-02-28 14:59:06

标签: c# xml serialization

在我们的应用程序中,我们有一个Money类型,其中包含金额(十进制)和货币代码(字符串)。以简单的形式,它看起来像这样:

public class Money
{
  public decimal Amount{get;set;}
  public string CurrencyCode{get;set;}
}

正如您可能想象的那样,它会在应用程序的许多位置使用,并且在发送到客户端或从客户端发送时经常被序列化/反序列化。货币金额通常被序列化为复合值,例如“1.23USD”代表1.23美元。旧客户端(HTML / JS)会将该值解析为其组件部分,返回Money类型。 Money值作为元素值发送,并作为属性值发送,具体取决于它们在应用程序中的位置,例如:

<SomeClass moneyValue="1.23USD" ... />

 <SomeClass>
<MoneyValue>1.23USD</MoneyValue>
...
</SomeClass>

我试图找出一种方法,我可以使用内置的C#/。NET Xml序列化工具来实现同样的行为。我看过实现ISerializable,但是还没有找到正确的方法。

基本上,我希望能够通过我自己的自定义逻辑(知道如何将“1.23USD”解析为Money金额)来反序列化Money金额,并将其序列化为简单的字符串,例如: “1.23USD”

最终目标是能够将课程中的金额作为:

[XmlAttribute]
public Money SomeField // SomeField='123.USD

或:

[XmlElement]
public Money SomeOtherField //<SomeOtherField>1.23USD</SomeOtherField>

就像使用int,string,double等简单类型一样。

这可能吗?

2 个答案:

答案 0 :(得分:3)

原来确实没有一个很好的100%答案 - 问题是你不能将复杂类型(例如Money)序列化为属性,即使你将它序列化为一个简单的字符串。所以无论如何,我都不能拥有一些可以在一般方面重复使用的属性='1.23美元'。

所以...发布的答案是属性的一个很好的解决方法,对于我使用Money作为元素的地方,我只是实现了IXmlSerializable来输出复合字段并在解析时重新拆分它。这样,不需要包装器,它只是工作。

public struct Money : IXmlSerializable
    {
        public double Amount { get; set; }
        public string CurrencyCode { get; set; }

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return (null);
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            reader.MoveToContent();

            Boolean isEmptyElement = reader.IsEmptyElement; // (1)
            reader.ReadStartElement();
            if (!isEmptyElement) // (1)
            {
                var str = reader.ReadContentAsString();
                string[] sa = str.Split(' ');
                Amount = double.Parse(sa[0]);
                CurrencyCode = sa[1];
                reader.ReadEndElement();
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteString(Amount + " " + CurrencyCode);
        }
    }

对于属性,发布的答案也可以,谢谢。

答案 1 :(得分:1)

以下代码可以解决问题:

public class Money
{
    public decimal Amount { get; set; }
    public string CurrencyCode { get; set; }
}

public class SomeClass
{
    public SomeClass() { }

    [XmlIgnore]
    public Money WrappedMoney { get; set; }

    [XmlAttribute]
    public string moneyValue
    {
        get
        {
            return String.Format("{0:.##}{1}", WrappedMoney.Amount, WrappedMoney.CurrencyCode);
        }
    }

}

public class ParentClass
{
    public SomeClass SomeClass {get; set;}
}

class Program
{
    public static int Main(string[] args)
    {
        var parent = new ParentClass
        {
            SomeClass = new SomeClass
            {
                WrappedMoney = new Money { Amount = 1.25M, CurrencyCode = "USD" }
            }
        };

        var serializer = new XmlSerializer(typeof(ParentClass));

        using (var writer = new StreamWriter("output.xml"))
        {
            serializer.Serialize(writer, parent);
        }

        return 0;
    }
}

Xml输出是:

<?xml version="1.0" encoding="utf-8"?>
<ParentClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <SomeClass moneyValue="1.25USD" />
</ParentClass>