Preventing Property Serialization
This is a little more complex than the straight forward scripts discussed in other similar questions posted on SO.
I have a class hierarchy like this:
-
DrawingObject
(abstract
class definesabstract
propertiesSize
andLocation
)-
Label
(inheritsDrawingObject
) Provides concrete implementations ofSize
andLocation
. Both properties must be serialized) -
Line
(inheritsDrawingObject
.Size
andLocation
properties should be ignored when serializing / deserializing)
-
I am using DataContractSerializer
my DrawingObject
s to serialize which creates the following problem:
- If I don't mark any of the classes with
DataContract
,IgnoreDataMember
has no effect, and the propertiesLocation
/ areSize
serialized forLabel
bothLine
. Something I don't want. - If I apply
DataContract
to my classes, a runtime exception is thrown indicating thatDrawingObject
it cannot be flaggedDataContract
because its base classObservableObject
(yes, MVVM Light) is not flaggedDataContract
.
What can I do to prevent this property from serializing in one derived class and not another?
Edit
The more I scour, the stranger it gets. It looks like the .NET Framework 3.5 has slightly changed the rules and [DataContract]
and the [DataMember]
attributes are no longer required to make it work DataContractSerializer
. If you omit these attributes, DataContractSerializer
all public read / write properties of the class will be serialized (the class must have a public constructor with no parameters). This may be good news for my scenario, but it looks like C # and VB.NET act differently in this regard:
FROM#
The following code is serialized correctly:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
namespace ConsoleApp1
{
public abstract class DrawingObject
{
public abstract string Location { get; set; }
public abstract string Size { get; set; }
}
public class Label : DrawingObject
{
public Label() { }
private string _Loc;
private string _Sz;
public override string Location { get { return _Loc; } set { _Loc = value; } }
public override string Size { get { return _Sz; } set { _Sz = value; } }
}
public class Line : DrawingObject
{
public Line() { }
public override string Location { get { return "Line Location"; } set { Console.WriteLine("Line.Location.set"); } }
public override string Size { get { return "Line Size"; } set { Console.WriteLine("Line.Size.set"); } }
}
class Program
{
static void Main(string[] args)
{
DrawingObject D1 = new Label() { Location="Label Loc", Size="Label Sz" } ;
DrawingObject D2 = new Line();
List<DrawingObject> DObjs = new List<DrawingObject>();
DObjs.Add(D1);
DObjs.Add(D2);
DataContractSerializer S = new DataContractSerializer(typeof(List<DrawingObject>), new[] { typeof(Line), typeof(Label) }, 0x7FFF, false, true, null);
var sb = new System.Text.StringBuilder();
using (var writer = new StringWriter(sb))
{
using (var xmlWriter = System.Xml.XmlWriter.Create(writer, new System.Xml.XmlWriterSettings() { Indent = true, OmitXmlDeclaration = false }))
S.WriteObject(xmlWriter, DObjs);
Console.WriteLine(sb.ToString());
Console.Read();
}
}
}
}
VB.NET
This code doesn't serialize anything:
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Xml.Serialization
Public MustInherit Class DrawingObject
Public MustOverride Property Location() As String
Public MustOverride Property Size() As String
End Class
Public Class Label
Inherits DrawingObject
Public Sub New()
End Sub
Private _Loc As String
Private _Sz As String
Public Overrides Property Location() As String
Get
Return _Loc
End Get
Set
_Loc = Value
End Set
End Property
Public Overrides Property Size() As String
Get
Return _Sz
End Get
Set
_Sz = Value
End Set
End Property
End Class
Public Class Line
Inherits DrawingObject
Public Sub New()
End Sub
Public Overrides Property Location() As String
Get
Return "Line Location"
End Get
Set
Console.WriteLine("Line.Location.set")
End Set
End Property
Public Overrides Property Size() As String
Get
Return "Line Size"
End Get
Set
Console.WriteLine("Line.Size.set")
End Set
End Property
End Class
Module Module1
Sub Main()
Dim D1 As DrawingObject = New Label() With {.Location = "Label Loc", .Size = "Label Sz"}
Dim D2 As DrawingObject = New Line()
Dim DObjs As New List(Of DrawingObject)
DObjs.Add(D1)
DObjs.Add(D2)
Dim S As New DataContractSerializer(GetType(List(Of DrawingObject)), {GetType(Line), GetType(Label)}, &H7FFF, False, True, Nothing)
Dim sb = New System.Text.StringBuilder()
Using writer = New StringWriter(sb)
Using xmlWriter = System.Xml.XmlWriter.Create(writer, New System.Xml.XmlWriterSettings() With {.Indent = True, .OmitXmlDeclaration = False})
S.WriteObject(xmlWriter, DObjs)
Console.WriteLine(sb.ToString())
Console.Read()
End Using
End Using
End Sub
End Module
I tried to make them syntactically equivalent, but DataContractSerializer
behaves differently.
Edit 2
I tested @CodeCaster suggestion and applied [IgnoreDataMember]
to Location
object property Line
. Doesn't matter (property Location
is still serialized for Line
). Looks like it DataContractSerializer
doesn't respect this attribute in derived classes. I also tried serializing the Line object directly and not the parent List
. Location
even then logs out.
Don't know where to go from here.
Edit 3
After digging around for a day and trying everything out, the difference between the C # and VB.NET code above finally turned out to be an issue when erasing an XML record. The fuzzy thing is that C # code doesn't require me to call Flush()
after serializing the object, whereas VB.NET only produces output if the call Flush()
is made after WriteObject()
.
Another thing I found is that it IgnoreDataMember
does not affect overridden members in derived classes. You have to apply the attribute in the base class for it to work, which is of course not possible in my case. Think I'll have to come up with some kind of problem in this problem.
source to share
You must use "KnownType" in order for the DataContractSerializer to serialize properly, so your base class must have a DataContract attribute and inform the types of the child classes as an example
[DataContract]
[KnownType(typeof(Label))]
public abstract class DrawingObject
{
public abstract string Location { get; set; }
public abstract string Size { get; set; }
}
source to share