Wednesday, March 23, 2011

Deep xml serialize a struct Options

Hi, I have the class and struct shown below. If I serialize the class as is using xmlserializer I get:

< Test>
< TestNumber1 >5< /TestNumber1 >
< InnerTest / >
< /Test >

what is the easiest way to make InnerTest serialise properly (preferably using xmlserializer) with out giving the Number property a setter?

Thanks, Nick

public class Test 
{ 
    private InnerTest innerTest; 
    private int testNumber; 


    public Test() 
    { 
        this.innerTest = new InnerTest(); 
        this.testNumber = 5; 
    } 


    public int TestNumber1 
    { 
        get { return this.testNumber; } 
        set { this.testNumber = value;} 
    } 


    public InnerTest InnerTest 
    { 
        get { return this.innerTest; } 
        set { this.innerTest = value;} 
    } 


} 


public struct InnerTest 
{ 
    private int number; 


    public InnerTest(int number) 
    { 
        this.number = number; 
    } 
    public int Number{get { return number; }} 
}
From stackoverflow
  • I've never done it myself, but I suspect you just need to implement the IXmlSerializable interface.

    As far as I can tell, that means you have to make your struct mutable - which is a pain. Ideally the XmlSerializer should recognise if your type has a constructor with a certain signature, but as that doesn't seem to be an option (as far as I can see) it would be worth implementing the interface using explicit interface implementation to at least discourage users from using it directly themselves.

    I wonder how other structs (e.g. DateTime) manage... perhaps they have explicit support in XmlSerializer.

  • As Jon Skeet quite correctly says, you'll need to use IXmlSerializable with the XmlSerializer if you don't want to have a public get/set for the properties because it's designed to work in a partial-trust environment so won't access any data that you couldn't have accessed anyway (to answer his last part - yes, some structs like DateTime do have explicit support in that serializer).

    Depending on what you're trying to achieve, and what version of .NET you're using, you could consider using the DataContractSerializer which doesn't require things to be public (for example you could put a DataMemberAttribute on a private field, or a property with public getter and private setter). This serializer gives you less control over the XML format (in fact it's very restrictive - for example it doesn't even support attributes!) but is somewhat faster as payback.

    (I've long hankered over something that's a combination of the two, i.e. the flexibility of XmlSerializer with the ability to serialize private members like DataContractSerializer but unfortunately there isn't one at the moment.)

  • If possible in this scenario, I would use DataContractSerializer (.NET 3.0), and I would use something like:

    [DataMember]
    public int TestNumber1 
    { 
        get { return this.testNumber; } 
        set { this.testNumber = value;} 
    } 
    
    // note **not** a data-member
    public InnerTest InnerTest 
    { 
        get { return this.innerTest; } 
        set { this.innerTest = value;} 
    } 
    
    [DataMember]
    private int InnerTestValue
    {
        get {return innerTest.Number;}
        set {innerTest = new InnerTest(value);}
    }
    

    thus side-stepping the issue. You can do similar with XmlSerializer, but you'd need to make InnerTestValue public (although you could decorate it with [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] - but this isn't ideal).

    Of course, if you have multiple values on the struct... trickier. You can have multiple shim properties, of course, but that is a bit scrappy. Basically, [de]serialization and immutable objects (as structs should be) don't mix all that well.

    Another option is to maintain a separate POCO version that uses mutable classes throughout, and translate between the two; again, not very attractive as an option for large object models.

  • Hi Nick,

    You can definitely use the XmlSerializer on your object there and get the expected result:

    Test test = new Test { TestNumber1 = 5 };
    
    XmlSerializer xmlSer = new XmlSerializer(typeof(Test));
    MemoryStream memStm = new MemoryStream();
    
    xmlSer.Serialize(memStm, test);
    

    To verify the result, read out the memory stream again into a string and look at it in the debugger (or write it out to a file):

    StreamReader stmR = new StreamReader(memStm);
    memStm.Position = 0;
    string output = stmR.ReadToEnd();
    

    If you don't do anything special, all public properties of the class you're serializing will be rendered as XML elements ..... in your resulting XML.

    There's a bunch of attributes like [XmlIgnore] and many more to tweak that as needed.

    Enjoy!

    Marc

0 comments:

Post a Comment