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
Related
I have a serializable class and one of the properties in my class generates a Guid in the getter. The property implements no setter and is ignores during serialization. Why is that and do I always have to implement a setter in order for my property to be serialized.
[Serializable]
public class Example
{
[XmlAttribute("id")]
public string Id
{
get
{
return Guid.NewGuid().ToString();
}
}
}
I tried implementing an empty setter and it got serialized correctly.
[Serializable]
public class Example
{
[XmlAttribute("id")]
public string Id
{
get
{
return Guid.NewGuid().ToString();
}
set {}
}
}
Update:
Can you point out how should I define properties whose values never change or ones that the value for is generated internally?
It's a limitation of XmlSerializer it doesn't serialize read-only properties, what you have done in your second example is essentially the hack to get it to serialize, however, it's useless if you need it to deserialize later.
Alternatively you could switch to using DataContractSerializer, it's more flexible.
See "Introducing XML Serialization" in the MSDN documentation. Among other things, it says:
Items That Can Be Serialized
The following items can be serialized using the XmlSerializer class:
Public read/write properties and fields of public classes.
Classes that implement ICollection or IEnumerable.
Note:
Only collections are serialized, not public properties.
XmlElement objects.
XmlNode objects.
DataSet objects.
Also, see "Why XML-Serializable class need a parameterless constructor"
Also, IXmlSerializable
In addition to the above types which can be serialized by the XML Serializer, any type which implements the IXmlSerializable interface can be serialized and deserialized. In particular, this means that the XElement and XDocument types can be serialized.
See "IXmlSerializable Interface".
Limitation of XMLSerializer - Properties without setter can't be serialized.
But you can use DataContractSerializer to serialize private setter properties -
[DataMember]
public string Id
{
get
{
return Guid.NewGuid().ToString();
}
private set {}
}
if you want to have private setters, and have the object be serializable/deserializable, impliment ISerializable, and create a constructor like MyObject(SerializationInfo info, StreamingContext context). An example is found here.
Serialization attributes are used to serialize and deserialize objects.
XmlSerializer will assume that you don't need to serialize any property that doesn't have a setter.
Setter will be used when deserializing a string into an object, because an instance of the object needs to be created and then the setter will be used to populate the property value.
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.
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.
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
I am confused about the serialization sample from MSDN.
My confusion is in method GetObjectData (which is called during serialization), will the method,
serialize both the additional data (in method GetObjectData from AddValue) and the fields/properties of the class;
or just write the data in method GetObjectData without writing fields/properties of the class?
I have debugged seems (2) is correct -- no fields/properties data are serialized if GetObjectData method is used? Is that correct? (I am not an expert and just want to confirm here, but 100% confident about myself.)
Im not sure what you want to achieve but isn't easier to let C# do the work for you:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace Test
{
[Serializable]
public class TestObject
{
private String name;
private String note;
#region Getters/setters
public String Name
{
get { return name; }
set { name = value; }
}
public String Note
{
get { return note; }
set { note = value; }
}
#endregion
}
}
Now you can use the XmlSerializer or BinaryFormatter to (de)serialize the object
If you implement ISerializable, you are reasponsible for all data (i.e. scenario "2" in your question); nothing extra is serialized automatically. What is your requirement? Things like DataContractSerializer can be property-based, allowing you to decorate both the regular fields and your custom property (that has some logic) and have them serialized properly. If you need binary (for space etc), then perhaps consider things like protobuf-net, which mixes the two while being space efficient.
So: what are your requirements?
Data Contract example:
[DataContract]
public class Foo {
[DataMember]
public int Bar {get;set;} // simple data
[DataMember]
private string DoSomeThinking {
get {.... serialize the complex data ....}
set {.... deserialize the complex data ....}
}
}
If you implement ISerializable you must add all data (at least the data needed to deserialize) including all fields to the SerializationInfo using AddValue.