How to exclude properties from serialization based on their values? - c#

To communicate with a web API, I have created a few classes which will be serialized to XML and sent to the API. The API only accepts these XMLs if they only hold properties with non-default values.
How can I leave out properties during serialization?
Suppose I have a class as follows (simplified example):
[XmlRoot("SomeData")]
public class SomeData
{
[XmlElement("rangeX")]
public int RangeX { get; set; }
[XmlElement("rangeY")]
public int RangeY { get; set; }
[XmlElement("rangeZ")]
public int RangeZ { get; set; }
}
An object which has a non-default value for RangeX and RangeY should thus be serialized to an XML which only holds tags for rangeX and rangeY.
I have found how to leave out null values, but this is not what I want - the default value could very well be different from null.
Thanks!

You can use the "secret" ShouldSerializeXxx() method:
public bool ShouldSerializeRangeX()
{
return RangeX != someDefaultValue;
}
There are a ton of examples when you search for ShouldSerialize Default Value, it's just hard to find if you don't know what you're looking for.
Here is a link to help get you started : Defining Default Values with the ShouldSerialize and Reset Methods

You can gain full control on how the objects of your class are serialized when you implement the IXmlSerializable interface (only including the code to write the data, since that is your immediate question):
public class SomeData : IXmlSerializable
{
public int RangeX { get; set; }
public int RangeY { get; set; }
public int RangeY { get; set; }
public void WriteXml (XmlWriter writer)
{
writer.WriteStartElement("SomeData");
if (RangeX != 0)
{
writer.WriteElementString("rangeX", RangeX.ToTring());
}
if (RangeY != 0)
{
writer.WriteElementString("rangeY", RangeY.ToTring());
}
if (RangeZ != 0)
{
writer.WriteElementString("rangeZ", RangeZ.ToTring());
}
writer.WriteEndElement();
}
public void ReadXml (XmlReader reader)
{
//Implement if needed
throw new NotImplementedException();
}
public XmlSchema GetSchema()
{
return null;
}
}

Related

Azure Service-Fabric and DocumentDB message serialization issue

So, - in my DocumentDB I may have the following document:
{
"id": 1,
"type": "A",
"content": {
"x": 1,
"y": 2
}
}
That may be backed by this model:
public class acontent
{
public int x { get; set; }
public int y { get; set; }
}
public class document
{
public int id { get; set; }
public string type { get; set; }
public object content { get; set; }
}
public class documenta : document
{
public new acontent content { get; set; }
}
The idea here is that document is a complex object where content may vary depending on type.
Now, - in my ServiceFabric application I have a stateless microservice that reads from DocumentDB and should return a document type object when called from the ServiceProxy.
The problem in this is that the DocumentQuery from the DocumentDB SDK, uses Json.NET serializer when querying the database, whilst servicefabric uses DataContractSerializer for serializing the service-messages.
So when the content part of document class is being deserialized from the DocumentDB it becomes:
Newtonsoft.Json.Linq.JObject
But when it is serialized back through the returned service-message you get the exception:
Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data
contract which is not supported. Consider modifying the definition of
collection 'Newtonsoft.Json.Linq.JToken' to remove references to
itself.
To illustrate this issue try the folowing code:
using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;
using Newtonsoft.Json;
namespace jsoinissue
{
public class acontent
{
public int x { get; set; }
public int y { get; set; }
}
public class document
{
public int id { get; set; }
public string type { get; set; }
public object content { get; set; }
}
public class documenta : document
{
public new acontent content { get; set; }
}
public class Program
{
private const string JSON_A = "{\"id\":1,\"type\":\"A\",\"content\":{\"x\":1,\"y\":2}}";
private static string SerializeObject<T> (T obj)
{
try
{
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(T));
using (var ms = new MemoryStream())
{
js.WriteObject(ms, obj);
ms.Position = 0;
using (var sr = new StreamReader(ms))
return sr.ReadToEnd();
}
}
catch (Exception e)
{
return String.Format("EXCEPTION: {0}",e.Message);
}
}
public static void Main()
{
var A = JsonConvert.DeserializeObject<document>(JSON_A);
var a = SerializeObject<document>(A);//HERE BE TROUBLE
Console.WriteLine(a);
Console.ReadKey();
}
}
}
How could I best resolve this issue?
Your basic problem is that DataContractJsonSerializer does not support untyped, free-form JSON data. As explained in Working with untyped JSON in a WCF service the System.Json namespace was added to Silverlight for this purpose, but it seems that it never made it into the full .Net class library.
Instead, in your stateless microservice can do a nested serialization where the free-form JSON is represented as an escaped string literal when serializing using the data contract serializer. Thus your classes would look something like this:
[DataContract]
[JsonObject]
public abstract class documentbase
{
[DataMember]
[JsonProperty]
public int id { get; set; }
[DataMember]
[JsonProperty]
public string type { get; set; }
[IgnoreDataMember]
[JsonProperty("content")]
public abstract JToken JsonContent { get; set; }
[JsonIgnore]
[DataMember(Name = "content")]
string DataContractContent
{
get
{
if (JsonContent == null)
return null;
return JsonContent.ToString(Newtonsoft.Json.Formatting.None);
}
set
{
if (string.IsNullOrEmpty(value))
JsonContent = null;
else
JsonContent = JToken.Parse(value);
}
}
}
[DataContract]
[JsonObject]
public class document : documentbase
{
JToken content;
public override JToken JsonContent { get { return content; } set { content = value; } }
}
[DataContract]
[JsonObject]
public class document<T> : documentbase where T : class
{
[IgnoreDataMember]
[JsonIgnore]
public T Content { get; set; }
public override JToken JsonContent
{
get
{
if (Content == null)
return null;
return JToken.FromObject(Content);
}
set
{
if (value == null || value.Type == JTokenType.Null)
Content = null;
else
Content = value.ToObject<T>();
}
}
}
Then the JSON generated by SerializeObject<document>(A) will look like:
{
"content":"{\"x\":1,\"y\":2}",
"id":1,
"type":"A"
}
Then, on the receiving system, you can deserialize to a document using the data contract serializer, then query the deserialized JToken JsonContent with LINQ to JSON. Alternatively, if the receiving system knows to expect a document<acontent> it can deserialize the data contract JSON as such, since document and document<T> have identical data contracts.
Have you looked into changing away from DataContractSerializer to a serializer with better support instead? Here's how you'd plug in a different serializer.
class InitializationCallbackAdapter
{
public Task OnInitialize()
{
this.StateManager.TryAddStateSerializer(new MyStateSerializer());
return Task.FromResult(true);
}
public IReliableStateManager StateManager { get; set; }
}
class MyStatefulService : StatefulService
{
public MyStatefulService(StatefulServiceContext context)
: this(context, new InitializationCallbackAdapter())
{
}
public MyStatefulService(StatefulServiceContext context, InitializationCallbackAdapter adapter)
: base(context, new ReliableStateManager(context, new ReliableStateManagerConfiguration(onInitializeStateSerializersEvent: adapter.OnInitialize)))
{
adapter.StateManager = this.StateManager;
}
}
This could be newtonsoft or whatever. Also I believe that the method is currently marked "Deprecated" however there's no alternative, so if it solves your problem go ahead and use it.

Serialize ValueObject as XML

You would think this is a very simple question. But after lots of searching and testing I have not found a pretty solution.
As per domain driven design I created strongly typed identifier classes. Because that helps with validation and not accidentally mixing up identifiers.
So I made a simple base class like so:
public abstract class UIDBase<T>
{
private readonly T _id;
protected UIDBase(T id)
{
_id = id;
}
public T ID
{
get { return _id; }
}
// Plus hash/equals implementation
}
And simple implementations like so:
public class SensorUID : UIDBase<string>
{
public SensorUID(string id) : base(id)
{
}
}
And then I use it:
public class SomeObject
{
public SensorUID SensorUID { get; set; }
public FileName FileName { get; set; }
}
But now when I serialize it as XML, I need a default constructor (annoying). But worse, the properties are no longer serialized as attributes.
Preferable I only want to add custom Xml serialisation code to my UIDBase class. Adding attributes or implementing an interface to tell the xml serializer that these classes should be serialized as attributes. Just like string/int/long etc.
If those value types can do it, why not my own value type?
The Xml should look like:
<SomeObject SensorUID="X" FileName="Y"/>
This seems to work, but it feels ugly:
public class SomeObject
{
[XmlIgnore]
public SensorUID SensorUID { get; set; }
[XmlIgnore]
public FileName FileName { get; set; }
[XmlAttribute("SensorUID")]
public string SensorUIDAsString
{
get { return SensorUID == null ? null : SensorUID.ID; }
set { SensorUID = new SensorUID(value); }
}
[XmlAttribute("FileName")]
public string FileNameAsString
{
get { return FileName == null ? null : FileName.ID; }
set { FileName = new FileName(value); }
}
}

Creating objects that initialize themselves based on a configuration string

I am coding a bunch of the following type of classes and it just seems a bit smelly to me. Basically I want to deserialze based on some json configuration of properties as well as serialize it for storage. I thought the following method would work well since I don't want to stipulate that the serialization/deserialization has to be json etc.
The code looks like this for a simple object:
public class IntegerDatasourceInstanceOptions
{
public int Start { get; set; }
public int Count { get; set; }
public IntegerDatasourceInstanceOptions()
{
}
public IntegerDatasourceInstanceOptions(string config)
{
var options = JsonConvert.DeserializeObject<IntegerDatasourceInstanceOptions>(config);
if (options != null)
{
Start = options.Start;
Count = options.Count;
}
}
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
Is this the correct way to go about this or should I use ISerializable instead?
I want to eliminate having to update all of the properties in the constructor. It's fine for a couple of properties in this case but if I have one with 30 properties it becomes a bit of a nightmare
I guess I'm just looking for some feedback as to whether this is the best way to go or not.
I tend to use a static method in instances like this, for example:
public class IntegerDatasourceInstanceOptions
{
public int Start { get; set; }
public int Count { get; set; }
public IntegerDatasourceInstanceOptions()
{
}
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
public static IntegerDatasourceInstanceOptions Create(string config)
{
return JsonConvert.DeserializeObject<IntegerDatasourceInstanceOptions>(config);
}
}
You can then just do:
var options = IntegerDatasourceInstanceOptions.Create("{...}");

How can the interface-driven serialization framework be improved?

I am developing an interface-driven serialization framework for use with interfaces for data XML serialization.
The ultimate goal is, that that framework is capable of save/load the state of the object (or a part of it, visible thought interface prism) using interfaces as an abstraction.
While thinking of the design, I have found, that using C# attributes, I can provide framework with required info to
save and load state of the object, and the way, how to perform a serialization/deserialization. As an advantages, I discovered that no solution exits, capable of partial object save/restore. If i describe the XML data in terms of an interface with data attributes, i have found that it is not requred anymore to have a dependency to that interface form the class (so that class does not require anymore to implement the given interface for the XML data).
I have a question about the usage of XmlObjectSerializer class, due to some usability issues. Here the example of the interface declaration, being tested:
Here is a primer:
interface IPersonRoot
{
string Name { get; set; }
}
[XmlRootSerializer("Body")]
interface IPersonCustomRoot
{
string Name { get; set; }
}
interface IPersonAttribute
{
[XmlAttributeRuntimeSerializer]
string Name { get; set; }
}
interface IPersonCustomAttribute
{
[XmlAttributeRuntimeSerializer("Id")]
string Name { get; set; }
}
interface IPersonElement
{
[XmlElementRuntimeSerializer]
string Name { get; set; }
}
interface IPersonCustomElement
{
[XmlElementRuntimeSerializer("Head")]
string Name { get; set; }
}
interface IPersonCustomElementString
{
[XmlElementRuntimeSerializer("Head", typeof(string))]
string Name { get; set; }
}
interface IVeryImportantPersonRoot : IPersonRoot
{
Guid Id { get; set; }
}
interface IVeryImportantPersonCustomRoot : IPersonCustomRoot
{
Guid Id { get; set; }
}
[XmlRootSerializer("Spirit")]
interface IVeryImportantPersonCustomRootOverride : IPersonCustomRoot
{
Guid Id { get; set; }
}
interface IVeryImportantPersonAttribute : IPersonAttribute
{
Guid Id { get; set; }
}
interface IVeryImportantPersonCustomAttribute : IPersonCustomAttribute
{
Guid Id { get; set; }
}
interface IVeryImportantPersonElement : IPersonElement
{
Guid Id { get; set; }
}
interface IVeryImportantPersonCustomElement : IPersonCustomElement
{
Guid Id { get; set; }
}
interface IVeryImportantPersonCustomElementOverride : IPersonCustomElement
{
[XmlElementRuntimeSerializer("Guid")]
Guid Id { get; set; }
}
interface IVeryImportantPersonCustomElementOverrideGuid : IPersonCustomElement
{
[XmlElementRuntimeSerializer("Guid", typeof(Guid))]
Guid Id { get; set; }
}
interface IVeryImportantPersonCustomRuntimeSerializer : IPersonCustomElement
{
Guid Id { get; set; }
[XmlColorRuntimeSerializer]
Color Color { get; set; }
}
interface IVeryImportantPersonColor
{
string Name { get; set; }
Guid Id { get; set; }
[XmlColorRuntimeSerializer]
Color Color { get; set; }
}
Here is the XmlObjectSerializer class definition:
public class XmlObjectSerializer
{
//...
public static void Load<T>(string xml, Type type, object value)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
XDocument root = XDocument.Load(XmlReader.Create(ms));
string rootName = GetRootName(typeof(T), root.Root.Name.ToString());
IXmlObjectSerializer serializer = Load(root.Root, CreateInternal(null, rootName));
serializer.Deserialize(type, value);
}
}
public static void Load<T>(string xml, object value)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
XDocument root = XDocument.Load(XmlReader.Create(ms));
string rootName = GetRootName(typeof(T), root.Root.Name.ToString());
IXmlObjectSerializer serializer = Load(root.Root, CreateInternal(null, rootName));
serializer.Deserialize(typeof(T), value);
}
}
public static string Save<T>(Type type, object value)
{
string rootName = GetRootName(typeof(T), value.GetType().Name);
XmlObjectSerializer result = CreateInternal(null, rootName);
IXmlRuntimeSerializer serializer = result;
serializer.Serialize(type, value);
XDocument root = new XDocument();
return result.ToXmlString();
}
public static string Save<T>(object value)
{
string rootName = GetRootName(typeof(T), value.GetType().Name);
XmlObjectSerializer result = CreateInternal(null, rootName);
IXmlRuntimeSerializer serializer = result;
serializer.Serialize(typeof(T), value);
return result.ToXmlString();
}
public static void Load(Stream data, Type type, object value)
{
XDocument document = XDocument.Load(XmlReader.Create(data));
string rootName = GetRootName(type, document.Root.Name.ToString());
IXmlObjectSerializer serializer = Load(document.Root, CreateInternal(null, rootName));
serializer.Deserialize(type, value);
}
public static void Save(Stream data, Type type, object value)
{
string rootName = GetRootName(type, value.GetType().Name);
XmlObjectSerializer serializer = CreateInternal(null, rootName);
serializer.Save(type, data, value);
}
}
Then, here is a question:
Is is better to change XmlObjectSerializer class to be more generic, like XmlObjectSerializer, or just hide all public methods, which uses an Type as as argument? I have the compiler static analyzer warning, saying that using a generic methods, (i.e. Load(...), Save(...)) in a non-generic class is a bad practices and should be changed to the method, accepting Type as an argument.
The problem is, should i change the design of a XmlObjectSerializer class following that rule (in this case, i will loose generic approach) and use object and Type as an arguments, or keep the methods intact.
Can you give me an advice to follow on, and code samples related to posted code sample?
Phanx,
I think that particular compiler warning in the general case is garbage; you should always look at the specific case.
Having written a few such frameworks, I would agree that it is important to include a Type based API, since there are many scenarios for such libraries where generics are a nuisance. By optimising for the non-generic case (rather than using MakeGenericMethod) you can do the other very simply:
Foo(Type type, ...) {...}
Foo<T>(...) { Foo(typeof(T), ...); }
This coming from someone currently re-writing a library to move the optimised case to Type rather than <T>. This is especially important for compact framework etc, where otherwise you can start getting missing-method exceptions when it runs out of space.
Re the interfaces; looks messy and complex - tl;dr; on that. But as long as you're happy...

How to make a value type nullable with .NET XmlSerializer?

Let's suppose I have this object:
[Serializable]
public class MyClass
{
public int Age { get; set; }
public int MyClassB { get; set; }
}
[Serializable]
public class MyClassB
{
public int RandomNumber { get; set; }
}
The XmlSerializer will serialize the object like that:
<MyClass>
<Age>0</age>
<MyClassB>
<RandomNumber>4234</RandomNumber>
</MyClassB>
</MyClass>
How can I made the property Age nullable? IE: to not serialize the property Age when it's under 0?
I tried with the Nullable, but it serialize my object like that:
<MyClass>
<Age d5p1:nil="true" />
<MyClassB>
<RandomNumber>4234</RandomNumber>
</MyClassB>
</MyClass>
By reading the MSDN documentation I found this:
You cannot apply the IsNullable property to a member typed as a value type because a value type cannot contain nullNothingnullptra null reference (Nothing in Visual Basic). Additionally, you cannot set this property to false for nullable value types. When such types are nullNothingnullptra null reference (Nothing in Visual Basic), they will be serialized by setting xsi:nil to true.
source: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.isnullable.aspx
I understand a value type can't be set to null. A valuetype is always set to something. The serialization can't make the decision to serialize it or not based on it's current value.
I tried with the attributes, but it didn't work out. I tried creating an agecontainer object and manipulate it's serialization with attributes, but it didn't work out.
What I really want is:
<MyClass>
<MyClassB>
<RandomNumber>4234</RandomNumber>
</MyClassB>
</MyClass>
When the property Age is below 0 (zero).
Looks like you'll have to implement custom serialization.
Yeah, that's what I though too, but I'd like to get away without it.
In the application, the object is much more complex, and I would like to not handle the serialization myself.
I just discovered this. XmlSerialier looks for a XXXSpecified boolean property to determine if it should be included. This should solve the problem nicely.
[Serializable]
public class MyClass
{
public int Age { get; set; }
[XmlIgnore]
public bool AgeSpecified { get { return Age >= 0; } }
public int MyClassB { get; set; }
}
[Serializable]
public class MyClassB
{
public int RandomNumber { get; set; }
}
Proof:
static string Serialize<T>(T obj)
{
var serializer = new XmlSerializer(typeof(T));
var builder = new StringBuilder();
using (var writer = new StringWriter(builder))
{
serializer.Serialize(writer, obj);
return builder.ToString();
}
}
static void Main(string[] args)
{
var withoutAge = new MyClass() { Age = -1 };
var withAge = new MyClass() { Age = 20 };
Serialize(withoutAge); // = <MyClass><MyClassB>0</MyClassB></MyClass>
Serialize(withAge); // = <MyClass><Age>20</Age><MyClassB>0</MyClassB></MyClass>
}
Edit: Yes, it is a documented feature. See the MSDN entry for XmlSerializer
Another option is to use a special pattern to create a Boolean field recognized by the XmlSerializer, and to apply the XmlIgnoreAttribute to the field. The pattern is created in the form of propertyNameSpecified. For example, if there is a field named "MyFirstName" you would also create a field named "MyFirstNameSpecified" that instructs the XmlSerializer whether to generate the XML element named "MyFirstName".
Extending Samuel's answer and Greg Beech's comment to the case of a boolean property: if the property is of type bool then you can't write a simple test in the propertySpecified property.
A solution is to use a Nullable<bool> type, then the test in the propertySpecified property is simply property.HasValue. e.g.
using System.Xml.Serialization;
public class Person
{
public bool? Employed { get; set; }
[XmlIgnore]
public bool EmployedSpecified { get { return Employed.HasValue; } }
}
An alternative to using a nullable type for a numeric property (suggested by Greg Beech) is to set the value property to an invalid default value, such as -1, as follows:
using System.ComponentModel;
using System.Xml.Serialization;
public class Person
{
[DefaultValue(-1)]
public int Age { get; set; }
[XmlIgnore]
public bool AgeSpecified { get { return Age >= 0; } }
}
You can use XmlElementAttribute.IsNullable:
[Serializable]
public class MyClass
{
[XmlElement(IsNullable = true)]
public int? Age { get; set; }
public int MyClassB { get; set; }
}
This should help
Make Age int? and..
public bool ShouldSerializeAge() { return Age.HasValue; }
..it does mean adding the ShouldSerializeXXX methods to your class!
You need to do custom XML serialization; see IXmlSerializer.
public class MyClass : IXmlSerializable
{
public int Age { get; set; }
public MyClassB MyClassB { get; set; }
public System.Xml.Schema.XmlSchema GetSchema()
{
// http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
return null;
}
public void ReadXml(XmlReader reader)
{
if (reader.IsStartElement("Age"))
Age = reader.ReadContentAsInt();
var serializer = new XmlSerializer(typeof(MyClassB));
MyClassB = (MyClassB)serializer.Deserialize(reader);
}
public void WriteXml(XmlWriter writer)
{
if (Age > 0)
{
writer.WriteStartElement("Age");
writer.WriteValue(Age);
writer.WriteEndElement();
}
var serializer = new XmlSerializer(typeof(MyClassB));
serializer.Serialize(writer, MyClassB);
}
}
Forget about Nullable ... ShouldSerializeXXX is a pretty solution.
Here Age will be serialized upon your condition.
[Serializable]
public class MyClass
{
public int Age { get; set; }
public int MyClassB { get; set; }
#region Conditional Serialization
public bool ShouldSerializeAge() { return age > 0; }
#endregion
}
[Serializable]
public class MyClassB
{
public int RandomNumber { get; set; }
}
xsd.exe will autogenerate the XXXSpecified property and accessors if you set the 'minoccurs' attribute as 'minoccurs="0"' for an element ... if you are using a schema to define your xml/class

Categories

Resources