0

I used to have simple classes like below:

public class Product {
   public Money Price {get; set;}
}

public class Money {
   public decimal Amount {get; set;}
   public CurrencyEnum Currency {get;set;}
}

I stored serialized entities in the XML format in database. The stored XML looked more or less like below:

<Product>
  <Price>
     <Amount>1000</Amount>
     <Currency>USD</Currency>
  </Price>
</Product>

As my needs for displaying the currency changed, I switched to using NodaMoney library, and simply changed the type for the property (my mistake) in Product class. NodaMoney's type has a different structure when serialized:

<Product>
   <Price Currency="USD">100</Price>
</Product>

Which made it a breaking change and I get errors during deserialization. I'm using XmlSerializer and I need both old and new format of the Price field to deserialize properly into the new NodaMoney's type. Moreover there might be Currency node missing in some legacy entries, which I need to populate with some default value. What would be the best approach to do this?

3
  • Insert an XSLT transformation into your processing pipeline to normalise the XML into a single canonical format before deserializing it. It's a classic problem with "data binding" approaches that they are very bad at handling variety and change in the XML input, so a transformation step to get the data into a standard format is often a good investment. Commented Nov 17 at 15:04
  • What is your RDBMS where you store serialized entities? Commented Nov 17 at 17:33
  • @Ceres, did you have a chance to try the proposed solution(s)? Commented 2 days ago

3 Answers 3

2

Because you still have both sets of data potentially being used you have to still use both sterilizers.

Start attempting to serialize with your newest format. If that fails attempt to serialize with your old format. If that succeeds then convert your old structure into the new one. The biggest issue is how to fill out the fields that were not in the old format.

Adding version attributes to structures can be a way to avoid extra serializations in the future. Then all you would have to do is use a quick read to get the version and pick that serializer to do the work.

Sign up to request clarification or add additional context in comments.

Comments

1

There are at least a few options available

Data Transfer objects (DTO)

Use a separate set of classes used just for serialization. These are sometimes called dto (serialization) and model (everywhere else) classes. Dto objects are converted to corresponding model objects on loading, and back again when saving. This allow you to keep the old classes for serialization, while using the new classes everywhere else in your code. It can also make validation and other logic easier in your model classes if you do not need to care about serialization.

Custom IXmlSerializable implementation

Most serialization frameworks allow some way to extend the serialization/deserialization. I'm not that familiar with XML, but the approach seem to be to provide a IXmlSerializable implementation. See Implementing Custom XML Serialization/Deserialization of compound data type? for details.

Use a XSLT transform

You can write a xslt document that describes how to convert from the old to the new format, and use XslCompiledTransform to apply it. In my experience, xslt is rather painful to both write and read, so it might be difficult for your colleagues or future you to make changes without some time to re-familiarize yourself with the format.

Use different version formats

Keep both versions of your classes, and use some way to indicate if you should deserialize to the old or new version. This might be the file extension, database field, xml property on the root element etc. Then write c# code to convert from the old to the new format.

Conclusion

I tend to favor the DTO-pattern, but that is easier if it is used from the start. If you need to retrofit an existing project I would probably start with the custom IXmlSerializable approach.

Comments

1

Assuming that your RDBMS is SQL Server, it is possible to make XML retrieval dynamic via XQuery based on the XML structure, i.e. old and new. This way XML shape will always meet the NodaMoney's expectations.

Conceptually, it is similar to the @MichaelKay idea.

Also, XQuery handles missing currency with a default value.

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (id INT IDENTITY PRIMARY KEY, product XML);
INSERT @tbl (product) VALUES
(N'<Product>
  <Price>
     <Amount>1000</Amount>
     <Currency>USD</Currency>
  </Price>
</Product>'),
(N'<Product>
  <Price>
     <Amount>180</Amount>
  </Price>
</Product>'),
(N'<Product>
   <Price Currency="USD">100</Price>
</Product>');
-- DDL and sample data population, end

SELECT id,
    CASE 
        WHEN product.exist('/Product/Price[@Currency]') = 1 THEN product
        ELSE product.query('
            <Product>
                <Price Currency="{
                    if (/Product/Price/Currency) 
                    then string((/Product/Price/Currency/text())[1])
                    else ''USD''}">
                    {/Product/Price/Amount/text()}
                </Price>
            </Product>
        ')
    END AS TransformedXML
FROM @tbl;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.