Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Custom XML Serialization/Deserialization of compound data type?

In our application, we have a Money type, which contains an amount (decimal) and a currency code (string). In simple form, it looks like this:

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

As you might imagine, this gets used in many places in the application, and is frequently serialized/deserialized as it is sent to/from the client. The Money amount is typically serialized as a compound value, e.g. "1.23USD" to represent $1.23. The old client (HTML/JS) would parse that value into its component parts back into the Money type. Money values are sent as element values, and as attribute values, depending on where they are in the app, e.g:

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

or

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

I am trying to figure out a way that I can use the built-in C#/.NET Xml Serialization tools to have this same sort of behavior. I looked at implementing ISerializable, but haven't been able to figure out quite the correct way to do so.

Essentially, I want to be able to have the Money amount deserialized by custom logic of my own (that knows how to parse "1.23USD" into a Money amount), and serialize it to the simple string, e.g. "1.23USD"

The end goal would be to able to have the Money amount in a class as either:

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

or:

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

just the same way you can do that with simple types like int, string, double, etc.

Is this possible?

like image 539
JakeL Avatar asked Feb 28 '14 14:02

JakeL


People also ask

What is serialization and deserialization in XML?

Serialization is the process of converting an object into a form that can be readily transported. For example, you can serialize an object and transport it over the Internet using HTTP between a client and a server. On the other end, deserialization reconstructs the object from the stream.

What is the correct way of using XML deserialization?

As with the CreatePo method, you must first construct an XmlSerializer, passing the type of the class to be deserialized to the constructor. Also, a FileStream is required to read the XML document. To deserialize the objects, call the Deserialize method with the FileStream as an argument.

Which annotation is needed for serialization and deserialization of XML format in the model classes?

Jackson annotations are useful in defining and controlling the process of serialization and deserialization across various formats such as XML, JSON, and YAML.

What is XML serialization in C#?

XML serialization is the process of converting an object's public properties and fields to a serial format (in this case, XML) for storage or transport. Deserialization re-creates the object in its original state from the XML output.


1 Answers

Turns out there really wasn't a good 100% answer to this - the problem is that you can't serialize the complex type (e.g. Money) to an attribute, even if you serialize it to a simple string. So no matter what, I can't have someAttribute='1.23 USD' that is reusable in a general way.

So...the posted answer is a nice workaround for Attributes, and for places where I used Money as an element, I just implemented IXmlSerializable to output the compound field and re-split it on parse. This way, no wrappers are required, it just works.

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);
        }
    }

And for attributes, the posted answer works as well, thank you.

like image 50
JakeL Avatar answered Oct 20 '22 00:10

JakeL