Welcome to CrankyGoblin.Com Sign in | Join | Help

Public Class GeoffAppleby

Inherits Microsoft.VisualBasic.MVP : Implements IBrainFart
You Can't XmlSerialize Shadowed Properties Very Well

This is something new I learned tonight. It make sense when you think about it I guess, but it bit us at work (luckily, not directly my fault this time :) but something I had to find an answer to since I'm the XMLSerialization expert there.

Oh who am I kidding?? I'm the everything expert there. I have so much knowledge inside my head that absolutely everyone comes to me for help because there's nothing I can't do. No one knows more than me. And I'm always right.

...

*blink*

Oh. Sorry. Got a little carried away there. Delusions of grandeur and all that.

*cough*

Anyway, I thought I'd write down a trivial example of what happens, for future reference. This is extremely trivial, and probably not something you'd implement for real. But it gets the error to occur, so it'll do.

First, consider ClassA:

Public Class ClassA

  Private msThing1 As String

  Public Property Thing1() As String
    Get
      Return msThing1
    End Get
    Set(ByVal Value As String)
      msThing1 = Value
    End Set
  End Property

End Class

A simple property, sets and gets a string. Now we have ClassB, which inherits from ClassA:

Public Class ClassB
  Inherits ClassA

  Public Shadows Property Thing1() As Int32
    Get
      Return System.Convert.ToInt32(MyBase.Thing1)
    End Get
    Set(ByVal Value As Int32)
      MyBase.Thing1 = Value.ToString
    End Set
  End Property

End Class

We've wrapped the Thing1 String property in a shadowing Thing1 Int32 property. Nice huh? Very handy!

Next we need to serialize these classes for some reason. So we create a serializer based on those types and see what happens.

What happens? BOOM! She 'sploded! Here's my test code:

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    Dim oSerializer As System.Xml.Serialization.XmlSerializer
    Dim oBuilder As New StringBuilder
    Dim oWriter As New StringWriter(oBuilder)

    Try
      oSerializer = New XmlSerializer(GetType(ClassA), New System.Type() {GetType(ClassB)})
      oSerializer.Serialize(oWriter, New ClassA)
      Debug.WriteLine(oBuilder.ToString)
      oBuilder.Length = 0
      oSerializer.Serialize(oWriter, New ClassB)
      Debug.WriteLine(oBuilder.ToString)
    Catch ex As Exception
      Stop
    End Try

  End Sub

An exception gets thrown trying to create the XmlSerializer object. The two properties clash, for while one shadows the other, they're both public, and therefore serializable.

Avoiding the problem is tricky, depending on how you need to use the classes. My first thought was to not bother actually serializing out ClassB's Thing1 property - after all, it only forwards on the call in the end to the base class, so the base classes version should be adequate for serialization. Right?

Wrong. If we modify ClassB to include an XmlIgnore() attribute:

Public Class ClassB
  Inherits ClassA

  <XmlIgnore()> _
  Public Shadows Property Thing1() As Int32
    Get
      Return System.Convert.ToInt32(MyBase.Thing1)
    End Get
    Set(ByVal Value As Int32)
      MyBase.Thing1 = Value.ToString
    End Set
  End Property

End Class

When the test code is run, we still die when creating the serializer, but this time with a much stranger error:

File or assembly name j0mgowdd.dll, or one of its dependencies, was not found. The name of the dll will change each time, but it's always some random name.

People who've banged their heads against the XmlSerializer before will have this baby many a time. What it normally means is that while the source code for the temp assembly was generated, the compiler failed to compile the code for some reason. So the temp assembly didn't get created, but the serializer still tried to instantiate it (that's how I read it, anyway). But what was the real error? With help from the amazing Sells Brothers XmlPreCompiler, we can discover:

ybstkcwp.0.cs(21,50): error CS0030: Cannot convert type 'int' to 'string'
ybstkcwp.0.cs(116,37): error CS0029: Cannot implicitly convert type 'string' to 'int'

So by XmlIgnoring the property in ClassB, some code was generated that assigned ClassA's string property to ClassB's int property - which happens because the property was still exists, even tho it's ignored at the same time. Get me?

There's three ways I've found to modify the code so that it works.

  • Ignore ClassA's Thing1 property. But if you serialize a ClassA on it's own, you won't get the Thing1 property serialized at all.
  • Hide ClassA's Thing1 property by making it non-public (Private, Protected, etc). See previous point.
  • Rename ClassB's Thing1 property to something else. So much for OO patterns.

All of these cases, depending on the scenario in question, suck.

Me, I prefer the fourth option. Find a completely different way to get what you need. If you find yourself painted into this corner, any 'fix' is only a hack to make it through, and not a good solution overall. Think about what you're trying to achieve, and you'll possibly find a different way of achieving the same sort of outcome. In the problem we have at work, I'll definitely be recommending option 4 :)

Listening to: thinking in reverse - the dissociatives - (3:44)
Posted: Thursday, 23 June 2005 7:11 AM by Geoff Appleby
Filed under: ,

Comments

DawlinLi said:

it would be nice if you know C# but since you know everything, C# won't be too much of a problem for you. ;)
# June 23, 2005 10:45 AM

Geoff Appleby said:

Of course I know C#! But I also know enough to not USE C# :P :P
# June 23, 2005 4:40 PM

Joost Ploegmakers said:

You could add <xmlIgnore()> to the Thing1 property in class A and B

then create a property in class A like:
Thing1A
with the following attributes:
<EditorBrowsable(EditorBrowsableState.Advanced), _
XmlAttributeAttribute("thing1-a")>

and in class B:
Thing1B
with the attributes:
<EditorBrowsable(EditorBrowsableState.Advanced), _
XmlAttributeAttribute("thing1-b")>

these 2 new properties can use the same private field as the xml-ignored (aka original) property.

result:
in intellisense no new properties show, in serialization all values are persisted.

hope that helps ..
# December 14, 2005 3:37 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

To submit your comment, click on these pictures:
  • Geoff's pretty blue eyes
  • Geoff's mother on a booger hunt
  • Geoff's tongue
Gaptcha Image - No Peeking! Gaptcha Image - No Peeking! Gaptcha Image - No Peeking!
Gaptcha Image - No Peeking! Gaptcha Image - No Peeking! Gaptcha Image - No Peeking!
Gaptcha Image - No Peeking! Gaptcha Image - No Peeking! Gaptcha Image - No Peeking!
Can't recognise the people in these pictures? Look here for a quick introduction.
There's a time limit for you to get your comment submitted before this set of pictures expires. If you think it's been longer than 10 minutes, get some new pictures first (you won't lose what you've typed so far).
Get some new pictures 

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS