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

Jak*_*keL 5 c# xml serialization

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

public class Money
{
  public decimal Amount{get;set;}
  public string CurrencyCode{get;set;}
}
Run Code Online (Sandbox Code Playgroud)

就像您想象的那样,它在应用程序的许多地方都得到了使用,并且在发送到客户端或从客户端发送时经常被序列化/反序列化。货币金额通常序列化为复合值,例如“ 1.23USD”代表$ 1.23。旧客户端(HTML / JS)会将其值解析为其组成部分,再转换为Money类型。Money值作为元素值和属性值发送,具体取决于它们在应用程序中的位置,例如:

<SomeClass moneyValue="1.23USD" ... />
Run Code Online (Sandbox Code Playgroud)

要么

 <SomeClass>
<MoneyValue>1.23USD</MoneyValue>
...
</SomeClass>
Run Code Online (Sandbox Code Playgroud)

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

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

最终目标将是使某个类中的货币金额如下:

[XmlAttribute]
public Money SomeField // SomeField='123.USD
Run Code Online (Sandbox Code Playgroud)

要么:

[XmlElement]
public Money SomeOtherField //<SomeOtherField>1.23USD</SomeOtherField>
Run Code Online (Sandbox Code Playgroud)

就像您可以使用简单类型(例如int,string,double等)一样进行操作。

这可能吗?

Jak*_*keL 5

事实证明,这真的不是一个好的 100% 答案 - 问题是您无法将复杂类型(例如 Money)序列化为属性,即使将其序列化为简单字符串也是如此。所以无论如何,我不能有 someAttribute='1.23 USD' 可以以一般方式重复使用。

所以......发布的答案是一个很好的属性解决方法,对于我使用 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

对于属性,发布的答案也有效,谢谢。


Bal*_*ick 3

下面的代码可以解决这个问题:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)