NetDataContractSerializer Deserialization With New Property - c#

Lacking any real foresight, I've serialized a large set of data decorated only with Serializable using NetDataContractSerializer, and now I'd like to add a new field. What are my options?
The original class looks something like this (with a few levels of inheritance and quite a few fields):
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
}
And now I'd like to add another property, say something like:
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
public int IntId { get; set; }
}
Now when I update the class and go to deserialize, I receive an exception since the new field is not present, something like:
Exception thrown: 'System.Runtime.Serialization.SerializationException' in System.Runtime.Serialization.dll
Additional information: Error in line 1 position 601. 'Element' '_x003C_StringId_x003E_k__BackingField' from namespace 'http://schemas.datacontract.org/2004/07/QT' is not expected. Expecting element '_x003C_IntId_x003E_k__BackingField'.
Ok, so this makes sense since NetDataContractSerializer requires the same class. I can get around that using a DataMember attribute like:
[DataMember(IsRequired = false)]
The problem then is that switching to DataMember (as I should have done upfront, or used a different serializer) changes the implicit alphabetical ordering, and then most of my fields will silently not deserialize as is well known.
I've attempted to add an ordering that's inline with the ordering on disk manually (via Order properties on the attribute), but that doesn't appear to be respected either. (I don't see an order value I could match in the raw xml either.)
Are there any other options beyond writing something to load the xml and insert the missing node? (Or equivalently setup a parallel type and deserialize from one an re-serialize to another?) If not, I'll probably just load up with the current type and deserialize to JsonNet or protobuf, but am I missing anything more straightforward with DataMember/etc?

Marking a type with [Serializable] means that the type can be serialized by serializing its public and private fields -- not its properties. NetDataContractSerializer respects this attribute when present, serializing the fields as indicated. For an auto-implemented property the secret backing field is what is actually serialized.
When adding a new field, what one generally does to handle legacy data is to mark it with [OptionalField] to indicate that it won't always be present in serialization streams. In c# 7.3 and later, it's possible to do this to the secret backing field of an auto-implemented property by using a field-targeted attribute:
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
[field: OptionalField]
public int IntId { get; set; }
}
Prior to c# 7.3 there is no way to apply an attribute to the backing field of an auto-implemented property. Thus you need to make the backing field be explicit and add the attribute to it:
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
[OptionalField]
int intId;
public int IntId { get { return intId; } set { intId = value; } }
}
Notes:
As noted in the question, if a type is marked with data contract attributes then NetDataContractSerializer will use those in preference to the default [Serializable] contract and allow you to explicitly indicate properties to serialize (and provide names clearer than the secret backing field names).
Unfortunately it is not always practical to add data contract attributes to legacy types.
NetDataContractSerializer has not been ported to .NET Core / .NET 5 and likely never will be.

Related

Strange reference class generation when using WCF

I encountered an (what I consider to be) issue with a service reference generation.
Original class (example)
[Serializable()]
public class Foo
{
private int _Bar;
public int Bar
{
get { return _Bar; }
set { _Bar = value; }
}
public Foo()
{
this._Bar = 42;
}
}
I found it strange that the constructor was using the private backing field rather than using the public setter, so I refactored to this:
[Serializable()]
public class Foo
{
public int Bar { get; set; }
public Foo()
{
this.Bar = 42;
}
}
these two seem equivalent enough I believe... however when I regenerated my service reference that contains a reference to Foo... I received a compile error.
No reference/extension method for _Bar exists in Foo
Note this is only what I can remember of the compile error since this is only a generalized example of what I encountered. There was existing code reliant on this service reference, which somehow referenced Foo._Bar - even though it is private.
So... is this the expected behavior? My re-factored class even though looking equivalent to me... generated a reference class in a way I didn't expect.
I'm assuming because the private _Bar was referenced directly in the constructor, it was somehow serialized with the class even though it was private?
I'm worried about this behavior, as I did similar refactoring in numerous places in our code base - am I not understanding something about how the serializing classes works?
Edit:
I'm noticing that the original Reference file created on the Foo class looks like this:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Foo", Namespace="http://schemas.datacontract.org/2004/07/Foo")]
[System.SerializableAttribute()]
public partial class Foo: object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
[System.NonSerializedAttribute()]
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
private int _BarField;
[System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
public int _Bar {
get {
return this._BarField;
}
set {
if ((this._BarField.Equals(value) != true)) {
this._BarField = value;
this.RaisePropertyChanged("_Bar");
}
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
I guess I expected Bar to be the accessible property in the Reference file from the original class, not _Bar - but that assumption was incorrect in this case. Is there something I'm missing here? Why would the reference file be generated with the private _Bar as the property, rather than the public Bar which is used as a getter and setter for the private backing field?
This behavior occurs because you have marked your class with the Serializable attribute, but no data contract attributes. According to Types Supported by the Data Contract Serializer,
The following is a complete list of types that can be serialized:
Types marked with the SerializableAttribute attribute. Many types included in the .NET Framework base class library fall into this category. The DataContractSerializer fully supports this serialization programming model that was used by .NET Framework remoting, the BinaryFormatter, and the SoapFormatter, including support for the ISerializable interface.
So, how does this "serialization programming model" work? From the docs:
When you apply the SerializableAttribute attribute to a type, all private and public fields are serialized by default.
Thus you have (unintentionally) instructed the data contract serializer to auto-generate a contract that serializes private and public fields of your class, not the properties. Then when you switch your property to be auto-implemented, you are changing the name of the its backing field from _Bar to the name of the hidden backing field. In turn the contract inferred from the type contains a renamed member. You can see the change in the contract when you serialize to XML. Here is the original field being serialized in the original XML:
<Foo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Question29495337.V1">
<_Bar>42</_Bar>
</Foo>
And the backing field in the new XML:
<Foo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Question29495337.V2">
<_x003C_Bar_x003E_k__BackingField>42</_x003C_Bar_x003E_k__BackingField>
</Foo>
Then when you do add service reference in a client, Visual Studio auto-generates a data contract type with public properties named after the serializable data contract members. Since those members got named after the private fields, this promotes the private field names on the server to public property names in the client, making seemingly private aspects of your class public.
You have several ways to avoid this problem:
Extract the serializable type into a DLL and link it into both the client and server. In this case the auto-generated data contract won't matter.
Remove the [Serializable] attribute. Doing so will cause DataContractSerializer to infer a contract that serializes all public fields, and properties with public get and set methods.
If you cannot remove [Serializable], annotate the class with explicit data contract attributes. These will override the auto-generated Serializable contract and stabilize the contract member names.

c# - DataContract Serialization - Base Classes, Inheritance and Overrides

Good day everyone,
I'm an independent game developer who has, in the past, primarily worked with XNA and, at the other extreme, commercial toolsets. The reach of XNA is pretty limited, however, and I'm building a cross-platform abstraction layer to target multiple platforms.
To cut a long story short, I've needed xml serialization that's accessible more broadly than [Serializable], and I've been pointed to data contracts. I've been doing a lot of research, but can't find any good information about some of the basics of the system, pertaining to inheritance and overrides.
The crux of my question is...
[DataContract]
public class Node
{
private string name;
public string Name { get { return name; } set { name = value; } }
public virtual float Rotation { get { return 0f; } set { } }
}
[DataContract]
public class FancyNode : Node
{
private float rotation;
public override float Rotation { get { return rotation; } set { rotation = value; } }
}
If I serialize a 'FancyNode', will 'Rotation' be properly serialized, and will 'Name' be serialised?
Follow-up Question:
I meant to ask earlier, but couldn't recall at the time. How does the serializer handler overriden [IgnoreDataMember] properties? For example...
[DataContract]
public class Simple
{
[IgnoreDataMember]
public virtual string Value { get { return ""; } set { } }
}
[DataContract]
public class Complex : Simple
{
private string value;
public override string Value { get { return value; } set { this.value = value; } }
}
Would 'Value' in 'Complex' be serialized? Some answers are suggesting that if no [DataMember] tags are used, all properties will be serialized. If so, does the [IgnoreDataMember] attribute of the base class have any bearing?
Some thoughts presented in a few answers are unclear. Please let me clear them up. First, the serializer that uses DataContract can be used to serialize types that are not decorated with DataContract. You only need to inform the serailizer about them.
For example, the DataContractJsonSerializer class will serialize a type into a JSON object (usually a string.) All you need to tell it is the type of object you're serializing, and then any other types it may reference:
var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(MyClass), new Type[] { Type1, Type2, Type3 });
The serializer will automatically serialize each public field and property of the type. For more information, see http://msdn.microsoft.com/en-us/ms733127
DataContractAttribute Marked Types
Once you mark a type with the DataContractAttribute, you turn that type into an automatically known type (that you don't need to provide as a child type) and you turn it into the properties and fields into opt-in mode.
Therefore, you must decorate each field or property you want to be serialized with the DataMemberAttribute. This means that the IgnoreDataMemberAttribute is useless. It is, the serializer will not look for it, since anything not marked with the DataMemberAttribute is automatically ignored.
Unmarked DataContractAttribute Types
When serializing a type that does not have the DataContractAttribute applied, as previously stated, each public property or field will be serialized. Therefore, the IgnoreDataMemberAttribute is used here to prevent a property or field from being serialized.
As far as I know, DataContract is an 'opt-in' serialization method, i.e. stuff isn't serialized unless you decorate it (tell the serializer you want to serialize it)
So for the above example, you would need to add [DataMember] to the properties you wanted to serialize
With the standard Serializable attribute, the serializer looks at all fields and only ignores those which you mark as NonSerialized
Some examples here:
http://jamescbender.azurewebsites.net/?p=651
Check the notes section on this for info on what gets serialized and a rough outline of what happens:
http://msdn.microsoft.com/en-us/library/ms733127.aspx
Edit: Also I can't see any reason why any of the fields once marked as [DataMember] wouldn't be serialized properly. The DataContract method of serialization can also deal with circular references - something that other serialization sometimes has trouble with:
http://msdn.microsoft.com/en-us/library/hh241056.aspx
You should include the [DataMember] attribute on properties that should be exposed by the contract.
[DataContract]
public class FancyNode : Node
{
private float rotation;
[DataMember]
public override float Rotation { get { return rotation; } set { rotation = value; } }
}
Note that Windows Communication Foundation (WCF) uses the Data Contract Serializer to serialize and deserialize data (convert it to and from XML). So actually you are still using Xml Serialization.
No it wont serialize because of Opt-In approach, you have to explicitly apply DataMember to your base class. Data Serialization does not work automatically in case you Inherit in Child Class

Hiding base class property using new keyword in WCF

I have a Scneario like this in my Server DLL class library.
[DataContract]
public class Base
{
[DataMember]
public string Info { get; set; }
}
[DataContract]
public class Child : Base
{
[DataMember]
public new int Info { get; set; }
public int Save()
{
}
}
My WCF Proxy at client side creates a Reference class. It Renames "Info" to "Info1". And shows proper properties in Base class.My code compiles great. So far so good. When I try to run ChildProxy.Save() from my client it gives me error Saying
"There was an error while trying to serialize parameter http://tempuri.org/:info. The InnerException message was 'Type 'ClientServiceLayer.InfoService.Info' with data contract name 'ArrayOfInfo:http://schemas.datacontract.org/2004/07/Info_DLL' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
"
How to hide the property of base class in WCF?
UPDATE:
Here is the call on the client-side
InvoiceServiceClient infoProxy = new InfoServiceClient();
invId = invfoProxy.Save();
As stated here:
You can't. Although the Child class is "hiding" the Info property of its base class, the attribute is being read by the serializer.
You could try adding [DataMember(Name = "Info")] to the child class and see what happens.
You could use KnownType Attribute in your DataContract
MSDN Data Contract Known Types

Protobuf-net Exception with Unknown Subclasses

I have developed an application that is meant to send data from client to server and back etc. using serialized objects.
For this application, I decided that protobuf-net would be a good option (especially as it handles variable-length objects so well).
However, when sending an object from client to server or vica-versa, all I know is that the object will be some child class of 'ReplicableObject'. Hence, I am using:
Serializer.SerializeWithLengthPrefix(stream, ro, PrefixStyle.Base128);
Where 'ro' is an object of a type that subclasses from ReplicableObject.
However, I get this exception:
An unhandled exception of type 'ProtoBuf.ProtoException' occurred in
protobuf-net.dll
Additional information: Unexpected type found during serialization;
types must be included with ProtoIncludeAttribute; found MessageObject
passed as ReplicableObject
In this particular instance, I'm trying to send a MessageObject.
As there is precious little documentation for protobuf-net, I am stuck on what to do. I've tried a few attributes here and there to no avail.
Any help appreciated.
Edit: I should make it clear that the subclasses might not even be ones that I've written.
Protobuf is a contract-based serialization format, designed to be platform independent. As such, no type metadata is included on the wire as it would not apply between platforms. Even inheritance is not part of the core protobuf spec.
protobuf-net as a specific implementation introduces support for inheritance (via some smoke and mirrors), but ideally it should still be possible to define the expected types in advance - exactly the same as other serializers such as XmlSerializer or DataContractSerializer. This can be done by using [ProtoInclude(...)] to specify the anticipated concrete types.
If you genuinely can't tell the actual types in advance, there is also a DynamicType option, which writes the AssemblyQualifiedName into the stream. If you are interested in this route, then note that the "cross-platform" features of the format start to break down, but it is very useful for .NET-to-.NET purposes.
At the simplest, a wrapper such as:
[ProtoContract]
public class SomeWrapper {
[ProtoMember(1, DynamicType = true)]
public object Value {get;set;}
}
Wrap your object in that and it should behave (in v2 at least; DynamicType did not exist in v1). Full example:
[TestFixture]
public class SO7218127
{
[Test]
public void Test()
{
var orig = new SomeWrapper {Value = new SubType { Foo = 123, Bar = "abc"}};
var clone = Serializer.DeepClone(orig);
Assert.AreEqual(123, orig.Value.Foo);
Assert.AreEqual("abc", ((SubType) clone.Value).Bar);
}
[ProtoContract]
public class SomeWrapper
{
[ProtoMember(1, DynamicType = true)]
public BaseType Value { get; set; }
}
[ProtoContract]
public class BaseType
{
[ProtoMember(1)]
public int Foo { get; set; }
}
[ProtoContract]
public class SubType : BaseType
{
[ProtoMember(2)]
public string Bar { get; set; }
}
}

Using XmlIgnore on generated partial classes

I've got a LINQ 2 SQL generated class I'd like to expose through a webservice.
There are some internal properties I don't want to be available.
Normally I'd throw [XmlIgnore] in there but because the properties are in the generated half I can't do that.
I've been looking at using MetadataType following this post which looks like it should allow me to define the property attributes in another class.
My code looks something like this:
[MetadataType(typeof(ProspectMetaData))]
public partial class Prospect : ApplicationBaseObject
{
}
public class ProspectMetaData
{
[XmlIgnore]
public object CreatedDateTime { get; set; }
[XmlIgnore]
public object AmendedDateTime { get; set; }
[XmlIgnore]
public object Timestamp { get; set; }
}
I'm referencing this through an ASP.NET Web Service from a Silverlight Project.
The issue is that the [XmlIgnore] attributes are being ignored, those properties are being sent through.
Does anyone have any insight into what might be going wrong here? and what might be the best way to do this?
You can do this by passing your own XmlAttributeOverrides to the XmlSerializer, in the XmlAttributeOverrides you can change the XmlIgnore to true for the fields/properties you wish without touching the DBML generated code and it works like a charm, use the same overrides to deserialize as well...
Refer this link for more information
// Create the XmlAttributeOverrides and XmlAttributes objects.
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes();
attrs.XmlIgnore = true;
/* Setting XmlIgnore to true overrides the XmlIgnoreAttribute
applied to the following fields. Thus it will be serialized.*/
xOver.Add(typeof(Prospect), "CreatedDateTime", attrs);
xOver.Add(typeof(Prospect), "AmendedDateTime", attrs);
xOver.Add(typeof(Prospect), "Timestamp", attrs);
XmlSerializer xSer = new XmlSerializer(typeof(Prospect), xOver);
TextWriter writer = new StreamWriter(outputFilePath);
xSer.Serialize(writer, object);
AFAIK, MetadataTypeAttribute is not supported by XmlSerializer (although it would be nice - I've simply never checked). And as you say, you can't add member attributes in a partial class.
One option may be to make the generated properties non-public (private, protected or internal) - and name it something like TimestampStorage (etc) - then re-expose them (in the partial class) on the public API:
[XmlIgnore]
public object Timestamp {
get {return TimestampStorage; }
set {TimestampStorage = value; }
}
// and other properties
(since XmlSerializer only looks at the public API). The biggest problem here is that LINQ-to-SQL queries (Where etc) will only work against the generated columns (TimestampStorage etc). I've used this approach before with the member as internal, allowing my DAL class to use the internal property... but it is a bit of a fudge.
I agree with Marc. The easiest thing to do is mark them internal. Optionally, you can then re-expose them in the partial class with [XmlIgnore]. BTW, You can control the accessibility of the properties in the linq2sql designer. Get at the properties dialog and you'll see a place to set them

Categories

Resources