C# XML Deserialize ignore Default value attribute - c#

I am having a class with a set of properties that I use as user level settings. Each property has a Default Value attribute. Few has XmlIgnore attribute to avoid the serialization.
When serializing this class object as memory stream and it writes it correct, but while de-serializing it actually creates an object with all the properties default value which was not part of the serialized object. How can I ignore this default value initialization for few properties? Thanks in advance.
XmlSerializer serializer = new mlSerializer(typeof(DisplayPreferences));
DisplayPreferences newPrefs = null;
MemoryStream ms = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(prefs));
newPrefs = (DisplayPreferences)serializer.Deserialize(ms);
if (newPrefs != null)
{
newPrefs.CopyTo(Editor.prefs);
}
This app is built in .NET 4.6 (winforms)

Welcome to stackoverflow.
You've given very little information on how you are de-serializing them (a code snippet would help), but I assume you are de-serializing them into a typed object. If that is the case, then what you are experiencing is standard behavior. That is the whole point of the default value, you can't "partially de-serialize a typed object".
What you can do however is either:
de-serializing the object into a raw xmlObject, and write a custom serializer for it.
Make the properties nullable, this is typically where DTO's are useful.See below.
public class SomeTypedObjectDTO
{
public Guid? NullableGuid { get; set; }
public int? NullableInt { get; set; }
}
----UPDATE----
I tired to keep this in line with your code. Lets say you have the following object.
public class DisplayPreferences
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Level { get; set; }
public bool CanDisplay { get; set; }
}
And lets say you only want to serialize the "CanDisplay" and the "level" properties. You can create a DTO (Data Transfer object) for it, which is basically a stripped down version of the original object.
DTO:
public class DisplayPreferencesDTO
{
public int Level { get; set; }
public bool CanDisplay { get; set; }
}
Then I believe all you need to do is change the generic typeof() to use the DTO instead of the actual object.
XmlSerializer serializer = new mlSerializer(typeof(DisplayPreferencesDTO));
You can then map the DTO back to the original object when you like and if you like. You can either do this mapping manually or use a framework called Automapper. Automapper was explicitly designed for mapping DTO's.
If you need me to clarify anything let me know.
Happy coding!

You may want to make them as null? just make them to nullable prop
such as make a int to int?

Related

MessagePack-CSharp: Serializing properties with generic types

I am trying to serialize a class with a generic property using the MessagePack-CSharp package. Here's a minimum reproducible example of what I'm trying to do:
using MessagePack;
[MessagePackObject(keyAsPropertyName: true)]
public class Data<T>
{
T data { get; set; }
public Data(T data)
{
this.data = data;
}
}
Data<int> testData = new(1);
byte[] bytes = MessagePackSerializer.Serialize(testData);
Console.WriteLine(MessagePackSerializer.ConvertToJson(bytes));
That program, as written, will throw an exception about not being able to find a matched constructor. If I add a parameterless constructor with an empty body, it no longer throws -- but will return {} as the converted JSON, when I really want { "data": 1 }. Is it possible to accomplish this?
I was being really stupid, I need to specify public T data { get; set; }. Alternatively the MessagePack-CSharp README has information about how to tell the serializer to target private fields.

How to do Conditional Serialization using C# - NewtonSoft.Json

I am doing json serialization using NewtonSoft.Json
public class CommonBase
{
[JsonProperty(PropertyName = "u_customer_id")]
public long CustomerId { get; set; }
}
I want to do a conditional serialization so that if CustomerId value is 0, I want to set a blank value for CustomerId during json serialization. Since CommonBase is a base class and I am not able to change data type from long to string.
How can I achieve this?
You almost have the answer in your question title. What you are looking for is Conditional Property Serialization
You just need to add method named like this: ShouldSerialize + PropertyName. In your case method should look like:
public bool ShouldSerializeCustomerId()
{
return SomeCondition;
}
P.s. if you are creating base class, you probably want to have abstract class.
I have solved this issue by changing CustomerId property as nullable.
public long? CustomerId { get; set; }

protobuf-net: Handling nulls in IEnumerable? Via what attributes/settings?

Some sample code below. The interesting/problem case is the Data property in
Mad. This code blows up (null value in the enumerable). Also, it works if i don't use the static attributes but instead the runtime type model, where i put in member.SupportNull = true for the fields (which is the behaviour i want), so what am i missing in the attributes / settings? Google search seems to indicate this is an open issue with probuf-net? That the same functionality is not available via attributes?
As as aside, if someone could suggest a way - i really love the runtime type model, i want to use that everywhere with a nice compiled model... but with it i lose the object versioning that protocol buffers solves! (via explicit tags). Is there any good way to maintain object version compatibility (simply adding fields) without doing all the static notation with fixed tags?
Basically the key thing with the runtime model is the assignment of tag indices and i can't think of a way of handling versions without explicitly specifying the tag indices via attributes...
[ProtoContract]
[ProtoInclude(1, typeof(ing))]
public class Eff
{
[ProtoMember(2)]
public string gg { get; set; }
}
[ProtoContract]
public class ing : Eff
{
[ProtoMember(1)]
public int zz { get; set; }
}
[ProtoContract]
public class Mad
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public IEnumerable<ing> Data { get; set; }
[ProtoMember(3)]
public ing Single { get; set; }
}
private static void Main(string[] args)
{
var obj = new Mad
{
Name = "test"
,Data = new[] { new ing {gg = "ooga", zz = -101},null,new ing()}
,Single = new ing {gg = "abc", zz = -999}
};
var m = new MemoryStream();
Serializer.Serialize(m, obj);
m.Seek(0, SeekOrigin.Begin);
var copy = Serializer.Deserialize<Mad>(m);
}
Short answer, it seems unavailable via attributes.
Workaround i'm doing for now - for every single type of interest(including the whole inheritance hierarchy) - add it to the type model yourself (with default handling so that it processes attributes), then call .GetFields() and set .SupportNull = true for each field (or only the relevant one)

How to serialize a class with a complex property using XML serialization in .NET?

I want to serialize a class. In this class there's a property of type Class1 that in turn has its own properties.
public abstract class ComponentBase
{
[ToSerialize] //An attribute defined my me, indicating whether or not to serialize this property.
public ComponentArgs Parameters { get; set; }
}
public class ComponentArgs
{
public string WorkingPath { get; set; }
public IList<Language> Languages { get; set; }
public string ComponentOutputPath { get; set; }
}
The information serialized must be put into a Dictionary<string,string>, such as
ComponentSettings[str_Name] = str_Value;
The method used in reading this value is Reflection
// pinfo: Property Info got via Type.GetProperties();
componentSettings.Add(pinfo.Name, pinfo.GetValue((object)this, null).ToString());
The information after serialization is:
<Parameters>MS.STBIntl.Pippin.Framework.ComponentArgs</Parameters>
instead of the value of ComponentArgs.WorkingPath.
The solution I thought of is to append to the following line an if judgement:
componentSettings.Add(pinfo.Name, pinfo.GetValue((object)this, null).ToString());
if(pinfo is ComponentArgs)
componentSettings.Add(pinfo.Name, pinfo.GetValue(
(ComponentArgs)this, null).WorkingPath+"\n"+
LanguageList+"\n"+ //Language list is a concatinated string of all elements in the list.
(ComponentArgs)this, null).ComponentOutputPath+"\n"+
);
When deserializing, add a judgement of whether the value contains more than 2 "\n", if so, extract each value from the string.
But this way seems clumsy and much more like an workaround. I wonder if there's any more professional way of doing it? My reviewer is very particular and he won't accept such a solution. If you know a way, could you please share it with me? Thanks a lot.
There are lots of ways to use inbuilt serialization.
The simplest and oldest is the [Serializable] attribute that tells .NET to serialize the members.
You can also use the WCF [DataContract] attribute to serialize stuff.
There is also the IXMLSerializable interface which allows you to implement custom XML readers and writers for you classes.
The bottom line is, there is no need to roll your own - it has been done.

C# automatic property deserialization of JSON

I need to deserialize some JavaScript object represented in JSON to an appropriate C# class. Given the nice features of automatic properties, I would prefer having them in these classes as opposed to just having fields. Unfortunately, the .NET serialization engine (at least, by default) totally ignores automatic properties on deserialization and only cares about the backing field, which is obviously not present in the JavaScript object.
Given that there's no standard way to name backing fields and to be honest I don't even want to bother with the "let's create a JavaScript object that looks like it had C# backing fields" approach as it sounds a bit dirty, the only way I could serialize JavaScript fields to C# auto-properties if I could force the serialization engine to somehow ignore the backing field and use the property directly. Unfortunately, I can't figure out how this is done or if this can be done at all. Any ideas would be appreciated.
EDIT: Here's an example:
Javascript:
function Cat()
{
this.Name = "Whiskers";
this.Breed = "Tabby";
}
var cat = new Cat();
This is then serialized to "{Name: 'Whiskers'}".
The C# class:
[Serializable()]
public class Cat
{
public string Name { get; set; }
public string Breed { get; set; }
}
And the deserialization code, that fails:
new DataContractJsonSerializer(typeof(Cat)).ReadObject(inputStream);
And it is apparent from the exception that it fails because it is looking for the backing field.
EDIT2: Here's the exception, if that helps (no inner exceptions):
System.Runtime.Serialization.SerializationException
"The data contract type 'Test.Cat'
cannot be deserialized because the
required data members
'<Name>k__BackingField, <Breed>k__BackingField' were not
found."
What's happening here is the deserializer is trying to guess the name of your backing fields.
You can solve this by adding explicit mappings (DataContract/DataMember attributes) like this:
[DataContract]
public class Cat
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Breed { get; set; }
}
You can do this with JavaScriptSerializer found in the System.Web.Script.Serialization namespace:
JavaScriptSerializer serializer = new JavaScriptSerializer();
Cat c = serializer.Deserialize<Cat>(jsonString);
I have POCO objects with automatic properties and this works just fine.
EDIT: I wrote about JSON Serializers in .NET which compares this serializer with DataContractJsonSerializer.
baretta's answer solved the k__BackingField bloat for me. Just a tiny addendum that you can decorate this class to auto serialize into either XML or JSON in a similar way:
[Serializable, XmlRoot, DataContract]
public class Cat
{
[XmlElement]
[DataMember]
public string Name { get; set; }
[XmlElement]
[DataMember]
public string Breed { get; set; }
}
... and then use a DataContractJsonSerializer or XmlSerializer to prepare it for your endpoint.
I'm assuming you are passing data via a web service. If you are using the WebService class with the ScriptMethod attribute uncommented-out, the web service methods can read JSON natively. They even use the same JavaScriptSerializer that was mentioned above. If you are using WCF I'm a little more fuzzy on the logic.
But make sure your JSON object are returning data for EVERY property in your class. In your error, there is mention of a Breed property that is not in your example.
Also, on the JavaScript side, do to the dynamic nature of JavaScript it is easy to add new properties to your objects. This can sometimes lead to circular references. You should remove any extra data that you might have added (just as you are sending data via the web method, then add it again once you are done).

Categories

Resources