WCF excluding datamembers to be serialized in the Derived class - c#

I have the following classes. I want do not want the derived class to serialize "Name", however, the following code does not seem to work. if I declare an instance of the derived class, the "Name" property still gets serialized..
[DataContract]
public class Base
{
[DataMember]
public virtual string Name
{get; set; }
}
[DataContract]
public class Derived: Base
{
[IgnoreDataMember]
public override string Name
{get; set; }
}

what if you declare the property virtual in the Base class? And then override it in the derived class

Try using the ScriptIgnore (json) or XmlIgnore (xml) attributes, depending on what you're serializing to. That should prevent that member from being serialized.

If you want to try to use XmlSerializer here is the code
public class Base
{
[XmlIgnore]
public virtual string Name { get; set; }
}
public class Derived : Base
{
[XmlIgnore] //Remove this to include
public override string Name { get; set; }
}
MemoryStream memStream = new MemoryStream();
XmlSerializer xmlSer = new XmlSerializer(typeof(Derived));
xmlSer.Serialize(memStream, new Derived() { Name = "aaaa" });
MessageBox.Show(Encoding.UTF8.GetString(memStream.ToArray()));

Related

Is there an shortcut way to deserialize protobuf base classes

I am using protobuf-net version 2.3.2.0.
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
[ProtoInclude(12, typeof(SubClass))]
public class BaseClass
{
public string Id { get; set; }
public string Name { get; set; }
}
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class SubClass : BaseClass
{
public string PropertySub { get; set; }
}
As my base class is inherited from many child classes.
How can I avoid placing attribute [ProtoInclude] on my base classes so that props in my base class deserialize as expected.

Serialize to JSON a class that hides member of base class

There are two classes:
class Document {
public DocumentItem[] DocumentItemList { get; set; }
}
class DocumentViewModel : Document{
public new DocumentItemViewModel[] DocumentItemList { get; set; }
}
DocumentItemList in derived class hides DocumentItemList in base class.
When DocumentViewModel object is serialized to JSON:
DocumentViewModel instance = CreateObject(); // object gets created
string serializedContent = new JavaScriptSerializer().Serialize(instance);
there are two DocumentItemLists in serialized string:
{
"DocumentItemList": [{
... etc. ...
}],
"DocumentItemList": null
}
Why is it like that? This causes error, when data is deserialized.
(BTW, I tested serialization with Newtonsoft.JSON, and that serializer doesn't have this error).
In case you want to stick with JavaScriptSerializer, you may consider to use [JsonIgnore] attribute, on the property you want to be ignored, this is discussed about the shadowed properties in a thread here.
Here you go:
class Document {
public DocumentItem[] DocumentItemList { get; set; }
}
class DocumentViewModel : Document{
[JsonIgnore]
public new DocumentItemViewModel[] DocumentItemList { get; set; }
}

Compatibility of Serializable and DataContract objects using DataContractSerializer

I have two classes, one inherits another - base class is DataContract, child is Serializable. I want to move property from child to base class, but after moving it I get exception saying that during deserializing the child class BackingProperty is not found. How can I correctly move that property to base class?
Do you have the base class decorated with the KnownType attribute?
[KnownType(typeof(SuperClass))]
[DataContract]
public class BaseClass
{
...
}
It's because serialization for Serializable marked classes and DataContract a bit different.
Here is a serializer I used for the samples:
var ser = new DataContractSerializer(typeof(BaseClass), new List<Type>() { typeof(ChildClass) });
You can compare:
[DataContract]
public class BaseClass
{
[DataMember]
public string Name { get; set; }
}
[Serializable]
public class ChildClass: BaseClass
{
public string SecondName { get; set; }
public int Age { get;set; }
}
Xml for that class (using DataContractSerializer, which is standard for WCF ):
<BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>Ivan</Name>
<_x003C_Age_x003E_k__BackingField>1</_x003C_Age_x003E_k__BackingField>
<_x003C_SecondName_x003E_k__BackingField>Petrov</_x003C_SecondName_x003E_k__BackingField>
</BaseClass>
But when you move a property to base class, which is marked ad DataContract:
[DataContract]
public class BaseClass
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string SecondName { get; set; }
}
[Serializable]
public class ChildClass: BaseClass
{
public int Age { get;set; }
}
Here is xml for that case:
<BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>Ivan</Name>
<SecondName>Petrov</SecondName>
<_x003C_Age_x003E_k__BackingField>1</_x003C_Age_x003E_k__BackingField>
</BaseClass> <BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>Ivan</Name>
<SecondName>Petrov</SecondName>
<_x003C_Age_x003E_k__BackingField>1</_x003C_Age_x003E_k__BackingField>
</BaseClass>
As a result you have different element name in Xml (SecondName vs _x003C_SecondName_x003E_k__BackingField) and these two xml are really different.
It's better to use a common approach in you solution, to mark everything or Serializable or DataContract. I would recommend to use DataContract.
EDIT 1:
Of course you can try to make a trick with property naming:
[DataMember(Name = #"_x003C_SecondName_x003E_k__BackingField", IsRequired = true)]
public string SecondName { get; set; }
And XML will be quite the same in result. You can also try to expose feilds for Serializable objects instead of properties:
[Serializable]
public class ChildClass : BaseClass
{
public string SecondName;// { get; set; }
public int Age; //{get;set;}
}
And xml will be:
<BaseClass i:type="ChildClass" xmlns="http://schemas.datacontract.org/2004/07/Serialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>Ivan</Name>
<Age>1</Age>
<SecondName>Petrov</SecondName>
</BaseClass>
But, unfortunately, if you move your property from ChildClass to BaseClass the order of properties will be changed. But DataContractSerializer always enforces the order of elements during deserialization and as a result not all the fields will be deserialzed. But you can switch to use the XmlSerializer in your service, which supports unordered deserialization.
Finally: Marking all data contract with DataContract
Even if you mark both your classes with DataContract, DataMember:
[DataContract]
public class BaseClass
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string SecondName { get; set; }
}
[DataContract]
public class ChildClass : BaseClass
{
//[DataMember]
//public string SecondName { get; set; }
[DataMember]
public int Age {get;set;}
}
And then move a property from ChildClass to BaseClass, it will still not work. Your property will be null (or default value), as the order has been changed :(

Xml-attributes in interfaces and abstract classes

I found something that confused me today:
1. If I have this:
public interface INamed
{
[XmlAttribute]
string Name { get; set; }
}
public class Named : INamed
{
public string Name { get; set; }
}
It gives the following output (Name property serialized as element):
<Named>
<Name>Johan</Name>
</Named>
2. If I have this:
public abstract class NamedBase
{
[XmlAttribute]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
public override string Name { get; set; }
}
The XmlSerializer throws System.InvalidOperationException
Member 'NamedDerived.Name' hides inherited member 'NamedBase.Name',
but has different custom attributes.
The code I used for serialization:
[TestFixture]
public class XmlAttributeTest
{
[Test]
public void SerializeTest()
{
var named = new NamedDerived {Name = "Johan"};
var xmlSerializer = new XmlSerializer(named.GetType());
var stringBuilder = new StringBuilder();
using (var stringWriter = new StringWriter(stringBuilder))
{
xmlSerializer.Serialize(stringWriter, named);
}
Console.WriteLine(stringBuilder.ToString());
}
}
My question is:
Am I doing it wrong and if so what is the correct way to use xml attributes in interfaces and abstract classes?
Attributes are not inherited on overriden properties. You need to redeclare them.
Also in your first example the behavior is not the "expected" one as you declared XmlAttribute at the interface level and yet the serialized xml contains the value as an element. So the attribute in the interface is ignored and only info taken from the actual class matters.
I think you should xmlignore your abstract class property
public abstract class NamedBase
{
[XmlIgnore]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
[XmlAttribute]
public override string Name { get; set; }
}

DataContractSerializer requires parameterless constructor in abstract base class

When I have the following classes and I try to serialize a ConcreteClass instance with DataContractSerializer.WriteObject(..) I get an InvalidDataContractException.
public abstract class AbstractClass
{
protected AbstractClass(string text) { }
}
public class ConcreteClass : AbstractClass
{
public ConcreteClass() : base("text") { }
}
The serializer is instantiated with new DataContractSerializer(typeof(ConcreteClass).
Using XmlSerializer makes no problems.
Now when adding public AbstractClass() {}
both serializers work.
So why does the DataContractSerializer requires abstract base classes to have a parameterless contructor? Here it is stated that types can be serialized which "have a constructor that does not have parameters" which is true for ConcreteClass. I also added some code to this required constructor and i don't think that it is ever called during the serialization process.
The complete Exception says:
System.Runtime.Serialization.InvalidDataContractException : Type
AbstractClass' cannot be serialized. Consider marking it with the
DataContractAttribute attribute, and marking all of its members you
want serialized with the DataMemberAttribute attribute. See the
Microsoft .NET Framework documentation for other supported types.
It even works if i leave the parameterless constructor and instead use the proposed attribute. So why is there a difference and why there is an attempt to serialize an abstract class? Of course there could be things like properties in the abstract class but should't these be serialized together with a ConcreteClass instance (which inherit such things)?
Edit:
My exact code:
namespace SerilizationTest
{
public abstract class AbstractClass
{
public string StringProperty { get; set; }
//This constructor is required (although never called).
//If not present we get "InvalidDataContractException :
//Type AbstractClass cannot be serialized"
public AbstractClass()
{
Console.WriteLine("We won't see this.");
}
public AbstractClass(string text)
{
StringProperty = text;
}
}
public class ConcreteClass : AbstractClass
{
public ConcreteClass() : base("text") { }
}
class Program
{
static void Main()
{
var serializer = new DataContractSerializer(typeof(ConcreteClass));
var memStream = new MemoryStream();
serializer.WriteObject(memStream, new ConcreteClass());
memStream.Seek(0, SeekOrigin.Begin);
var deserializedObj = (ConcreteClass)serializer.ReadObject(memStream);
Console.WriteLine(deserializedObj.StringProperty);
}
}
}
The exception you get is saying that there is no [DataContract] attribute on the AbstractClass.
DataContract is an attribute that is used to mark classes that can be serializable. This attribute will only included attributes in your class to be serialized if you tell it to do so by using the DataMember attribute.
The DataMember attribute is used to mark which attributes you want in your serializable class.
This attribute is found at System.Runtime.Serialization;
For example...
public abstract class Bar
{
}
public class Foo : Bar
{
string one { get; set; }
string two { get; set; }
string three { get; set; }
}
If I try to serialize my Foo class then I will get your exeption. So if I add the DataContract attribute to my Bar class like the exception suggests the next time I try to serialize I will get the same error just pointing at another part of my code, the Foo class itself.
What I need to do is add DataContract to both like so.
[DataContract]
public abstract class Bar
{
}
[DataContract]
public class Foo : Bar
{
string one { get; set; }
string two { get; set; }
string three { get; set; }
}
So now what we have will be able to create a serialized file. However, there won't be any information as we never told our DataContract what to include.To fix this problem we add the DataMember attribute to the attributes in the class we want to include.
[DataContract]
public class Foo : Bar
{
[DataMember]
string one { get; set; }
string two { get; set; }
[DataMember]
string three { get; set; }
}
With the DataMember attributes added when this class is serialized it will only serialize the information about string one and string three. string two will not be included because it wasn't specifically marked with the DataMember attribute.

Categories