Optional properties when deserializing a DataContract/Serializable mish-mash - c#

I have an existing codebase that persists a couple of simple classes to disk via NetDataContractSerializer, but the classes are unfortunately not adorned with [DataContract], but rather with [Serializable]. This works fine, but now I want to add a few new properties to the persisted classes, while still be able to read the files generated by the old version.
Let's say this is the class:
[Serializable]
public class Persisted
{
public int OldProperty {get;set;}
public int NewProperty {get;set;}
}
Now, when I deserialize the old files, I get an exception because they don't contain NewProperty. This makes sense. So I wanted to have NewProperty ignored, but while there's a [OptionalField] attribute to have the serializer ignore the missing field, it can't be applied to properties - only fields.
So I figured I'll use [DataContract] and [DataMember], which also has an IsRequired property, but this changes the layout of the serialized file, and it can't read the old data files. Moreover, you can't mix [Serializable] and [DataMember] - if the serializer sees the [Serializable] attribute, it ignores the [DataMember] directives.
So, barring the option to do a one-time conversion of the old files (possible, but not my first choice), is there a way to get the NetDataContractSerializer to ignore a field in an existing XML serialized object?

The problem is that when using the Serializable attribute, what gets serialized are fields, not properties. Since you're using auto-properties, the fields are hidden and you can't add attributes to them.
The solution is simple - don't use auto-properties.

Related

C# generate XML for non-serializable properties

I'm having a lot of classes that I would like to export as XML for other applications to consume. The properties I want to export don't have a setter and the classes don't have a constructor without properties since I don't want this behaviour in my code. Therefore, it seems I can't use (XML) serialization on these classes and properties, even though I do want to export it into XML. I don't need deserialization though, as the serialization to XML is meant to be export-only.
I have tried XML serialization, but it appears this really only supports classes that can be used in both directions (serialization AND deserialization), which makes my classes not applicable. https://learn.microsoft.com/en-us/dotnet/standard/serialization/introducing-xml-serialization
Obviously, I could make serializable versions of each class, but doing this by hand would need to me to check manually after any updates of the original classes that I have updated the serializable classes. Additionally, I would need to write for every class, the code to transform is to its serializable version.
Is there a way to use the strength of XML serialization which takes care of all fuzz about XML, without needing the classes to be deserializable? Or do you have any other suggestions for easy ways to export XML for these classes and properties?
There's a really simple bit of guidance that applies to virtually every serializer, regardless of format (xml, json, protobuf, etc), implementation details, etc:
if your type model happens to be a 1:1 fit for the serializer, great! use it!
otherwise, don't try; create a completely separate model that is purely for serialization; the shape and behavior should be exactly what the serializer needs to get the result you want; then map between the two models (your domain model, and the serialization model) as needed
There is a caveat around "many serializers allow a custom serializer API", but in my experience, it usually isn't worth the pain, and switching to a separate serialization model is a better idea. This applies especially in the case of XmlSerializer, since IXmlSerializable is virtually impossible to implement absolutely correctly manually.
So; to be explicit here; if the problem is that your type lacks the correct constuctors and property setters to work with XmlSerializer : create a new model that has those things, and just shim between them.
The DataContractSerializer can serialize classes without a default constructor.
You will have to mark the class with DataContractAttribute in order to do so. Also, the properties to serialize need setters. The setters may be private, but the setter does have to exist:
[DataContract] // Need this to serialize classes without default constructor.
public class Person
{
public Person(string name, DateTime dob)
{
this.Name = name;
this.DateOfBirth = dob;
}
[DataMember] // Need this to serialize this property
public string Name { get; private set; } // Need setter for serializer to work
[DataMember]
public DateTime DateOfBirth { get; private set; }
}
Usage:
var person = new Person("Jesse de Wit", new DateTime(1988, 5, 27));
var serializer = new DataContractSerializer(typeof(Person));
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, person);
}

DataContract secret behavior?

Every time I meet this attribute, I always see such usage:
[DataContract]
class DataTransferObject
{
[DataMember]
public int Value {get;set;}
}
And in this example all inherited members should apply DataMember attribute to every property or field, this can lead to VERY clumsy and poilerplate code. But, recently I found (maybe secret feature?) a very elegant way of using it:
[DataContract]
public abstract class DTOBase
{
}
public class MyDTO : DTOBase
{
public int Value {get;set;}
public MyDTO(){} //important part is here
}
Important part: You should always explicitly define parameterless constructor, otherwise it won't serialize properly.
And yeah. It will serialize all its public members, no matter how deep will be inheritance, without need to apply attributes to members or class definitions.
Is this somehow documented somewhere (I didn't found)? Because, I were very supprized how much of boilerplate can be avoided.
Actually, you don't need to use DataContract and DataMember attributes if you don't want to, however they give you flexibility in defining what needs to be serialized and how.
I suggest starting with article Serializable Types on MSDN, it has a lot of information how Data Contract serializer works. Here are first 2 paragraphs, proving that you don't need to use attributes:
By default, the DataContractSerializer serializes all publicly visible
types.
All public read/write properties and fields of the type are
serialized. You can change the default behavior by applying the
DataContractAttribute and DataMemberAttribute attributes to the types
and members This feature can be useful in situations in which you have
types that are not under your control and cannot be modified to add
attributes. The DataContractSerializer recognizes such "unmarked"
types.
The main rules that apply to your case are:
DataContract attribute is not inherited. You can either apply it or not on your base class DTOBase, it is ignored in child class MyDTO. You can remove DataContract attribute from DTOBase class and result will be the same.
If you use DataContract attribute on a class, then only members that have DataMember attribute will be serialized. This is what happened in class DataTransferObject in your first sample.
If you do not use DataContract attribute on a class, then all public members of a class are serialized. This is what happened with your class MyDTO.

Set DataContract and DataMember Without All the Attributes

I find the [DataContract] and [DataMember] attributes a bit messy and would rather do this with code in a config method or something. Is this possible?
You don't have to use these attributes at all. DataContractSerializer will serialize all public properties with getter and setter but in case of serializing entities with navigation properties you will easily end with exception due to "cyclic reference".
To avoid that exception you must either use [DataContract(IsReference = true)] on your entity class with DataMember on every property you want to serilize or IgnoreDataMember on every property you don't want to serialize.
The last and the most complex option is avoiding attributes completely and custom classes implementing IDataContractSurrogate to control serialization outside of the type.
You can also write your completely custom serialization process or use XML serialization or binary serialization with all its requirements.
No, the DataContractSerializer is an opt-in serializer - you have to tell it what you want included.
With other serializers you need to use things like NonSerializedAttribute or XmlIgnoreAttribute to tell the serializer to leave things alone.
I know this is a rather old post, but I came here thinking the same thing if there is a way to set all member attributes automatically on some legacy code with public fields and no getters and setters.
What makes it look just a little bit less messy is shortening up the name DataMember:
using DM = System.Runtime.Serialization.DataMemberAttribute;
[DataContract]
public class SomeClass
{
[DM] public bool IsMO;
[DM] public string LabCode;
[DM] public string OrderNumber;
}

Fields vs. Properties and XMLSerializers (101)

So I've been studying the use of various Serializers in the .NET Framework and while trying to experiment on preventing certain objects in a class from being serialized I was thrusted back to some very basic programming questions that I "thought" I knew. Given this example:
public class Example
{
public string examName;
[XmlIgnore]
public int exampleNumber;
public Example()
{ }
[XmlIgnore]
public int ExampleNumberTwo { get; set; }
}
I can create an instance of this class and using the XMLSerializer can output the content of this class in XML format. The [XmlIgnore] attribute actually does what I'd expected; it prevents the serialization of the referenced items.
So venturing further I replaced the [XmlIgnore] declaration for "exampleNumber" with [NonSerializable] expecting the similar results but the output did not change. After searching through resources, it was stated that the [NonSerializable] attribute should only be used on fields and [XmlIgnore] attributes should be used on properties.
Yet another post stated that the [NonSerializable] attribute has no effect when using the XMLSerializer but will produce the expected results when using the SOAP or BinaryFormatter. So I'm lost on the concept at this point.
But this brought me to the basic question, what defines a field vs. a property? I know its a basic question and I've even viewed other discussions here but the degree of clarity I am looking for still wasn't really clear.
I can use the [XmlIgnore] attribute on the property (ExampleNumberTwo) or the variable (exampleNumber) so the statement that it can ONLY be used on Properties doesn't seem correct.
But then again, I have always referred to the objects in my example such as (examName) and (exampleNumber) as being member variables. So what exactly is the signature of a "Field"
Can anyone shed some light on this?
The MSDN documentation supports the idea that [NonSerialized] only gives the expected results with the binary and SOAP serializers:
When using the BinaryFormatter or SoapFormatter classes to serialize
an object, use the NonSerializedAttribute attribute to prevent a field
from being serialized. For example, you can use this attribute to
prevent the serialization of sensitive data.
The target objects for the NonSerializedAttribute attribute are public
and private fields of a serializable class. By default, classes are
not serializable unless they are marked with SerializableAttribute.
During the serialization process all the public and private fields of
a class are serialized by default. Fields marked with
NonSerializedAttribute are excluded during serialization. If you are
using the XmlSerializer class to serialize an object, use the
XmlIgnoreAttribute class to get the same functionality. Alternatively,
implement the ISerializable interface to explicitly control the
serialization process. Note that classes that implement ISerializable
must still be marked with SerializableAttribute.
In terms of "field" vs. "property", fields are straight data variables contained by a class. Properties are actually specially named methods on the class (get_PropName() and set_PropName()). In your code, the compiler allows you to use properties the same way you would use a field, and then inserts the appropriate get/set call for you.
Oftentimes, properties will be simple wrappers around a field:
private int myField;
public int MyProperty
{
get { return myField; }
set { myField = value; }
}
But they don't have to be:
public int TodaysDate
{
get { return DateTime.Today; }
}
In general, you want all your fields to be private, since they're supposed to be implementation details. Any simple data that you'd like to expose should be done via a property, since you can easily surround the data access with (changeable) logic.
In C#, the short answer is that properties have get and/or set methods, while fields do not. VB.NET makes it a little more evident by requiring the "Property" qualifier to be used to differentiate one.
With C#, you can just append " { get; set; }" to the end of a field's definition and it's now a property.
Where this really comes into play is in reflection. Fields and Properties are segregated from one another into different enumerable collections.
This answer to What are the differences between the XmlSerializer and BinaryFormatter will help you get started in the right direction.

Serialize List<> of classes declared with internal modifier?

I'm trying to add XML serialization to a fairly trivial class structure in C#. Essentially, there's a single instance of a root class (call it AClass), which holds a List of several instances of some other class (call it AnotherClass):
[XmlRoot("RootNode")]
public class AClass {
[XmlElement("ListNode")]
internal List otherObjects { get; set; }
}
public class AnotherClass {
[XmlAttribute("Name")]
internal string name { get; set; }
}
When serializing, I'd like for both these classes to be serialized together - that is, if I serialize AClass, its list of AnotherClass gets serialized as well (see this question).
I have this mostly working, but the problem is that during serialization, XmlSerializer only seems to want to deal with public properties of the class - it doesn't serialize AnotherClass at all if the list is declared internal.
I tried making the assembly's internals visible to the serializer:
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("System.Xml")]
That didn't seem to do anything. Is there a way I can get XmlSerializer to recursively serialize lists of objects that are declared internal?
You're on the right track... except that the actual serialization is not performed by System.Xml, but by a dynamically generated assembly. You can't predict the name of that assembly (it's randomly generated), so you can't use it in the InternalsVisibleTo attribute.
The only solution is to pre-generate the XML serialization assembly. You can do that using the XML Serializer Generator Tool (Sgen.exe). The name of the generated assembly will be "YourAssembly.XmlSerializers" ; that's the name you have to use in the InternalsVisibleTo attribute.
The idea of adding the InternalsVisibleTo attribute is a good one, but I think that the problem is that the Xml serialization code only looks for the public types in the assembly.
To my knowledge there is no way to make XmlSerializer serialize or deserialize internal types, and you will have to either declare the types as public or write your own serialization code.
Moreover, the XmlSerializer class documentation explicitly states that only public properties of the object will be serialized: "XML serialization is the process of converting an object's public properties and fields to a serial format (in this case, XML) for storage or transport", so it really looks like it's a design decision.

Categories

Resources