Deserialize collection type virtual data member - c#

I'm trying to deserialize json to RequestWithDefault object
JSON:
{
"fields":["f1","f2"]
}
My simple class diagram:
[DataContext]
public abstract class BaseRequest
{
[DataMember]
public virtual List<string> Fields { get; set; }
}
[DataContext]
public class RequestWithDefault : BaseRequest
{
[DataMember]
public override List<string> Fields {get; set; } = new List<string> {"test"}
}
After deserializing json to RequestWithDefault object Fields property contains ["test", "f1", "f1"]. I want to be sure that this default values are applied only in case when Fields were not specified in request, or was specified as null. How I can do this? I tried with [OnDeserializing] attribute but without success. Result is the same
According to this:
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/collection-types-in-data-contracts
Looks like during deserialization DataContractSerializer calling Add method from collection. That's why I have also default value and rest of items are added. When I will replace List<string> to string[] everything works fine.

It seems WCF serialization never use setter to set the value of the DataMember with type of collection, but use Add instead. Because of this, the only way to check whether the fields has any value is to check after it has been deserialized (not while deserializing).
[DataContext]
public abstract class BaseRequest
{
[DataMember]
public virtual List<string> Fields { get; set; }
}
[DataContext]
public class RequestWithDefault : BaseRequest
{
[System.Runtime.Serialization.OnDeserialized]
void OnDeserialized(System.Runtime.Serialization.StreamingContext c)
{
if (Fields == null
|| Fields.Count < 1)
{
Fields = new List<string> { "test" };
}
}
}

Related

c# REST Controller accepts range of names for complex object property

I have an endpoint with a complex object as an argument. When it is called a user supplies a JSON object.
public class ComplexObject
{
[JsonProperty("multiNamedProperty")]
public string MultiNamedProperty { get; set; }
public dynamic OtherProperty { get; set; }
public double NumberProperty { get; set; }
}
public JsonResult MethodName(ComplexObject poco)
{
this.ServiceName.PerformLogic(poco);
}
I want to be able to supply the endpoint a ComplexObject but have a set of names available for the property that all map like "name", "1", "banana", "n4m3", "fullName" etc.
Example expected object:
{
thisStringCouldBeAnything: "notNullValue",
bananaBananaTerracottaPie: {
banana: ["terracotta", "pie"],
terracotta: "pie"
},
numberProperty: 0
}
I suggest you use a mix of Dictionary and JDocument. But this would mean that you need to slightly restructure the expected object if you want to map it to class.
Example,
{
properties: {
thisStringCouldBeAnything: "notNullValue",
bananaBananaTerracottaPie: {
banana: ["terracotta", "pie"],
terracotta: "pie"
},
numberProperty: 0
}
}
C# class
public class ComplexObject
{
Dictionary<string,JDocument> Properties {get;set;}
}
Alternatively, you could directly use the Dictionary in the parameter without using the class,
public ActionResult GetSomething(Dictionary<string,JDocument> properties)
{
}

How to automatically include type information when serializing?

Is it possible to specify that I always want type-information in the json object when serializing a property in an class?
(Ideally with Newtonsoft).
I'm thinking something like this:
public abstract class Value {...}
public class BigValue : Value {...}
public class SmallValue : Value {...}
public class ValueContainer
{
[JsonSetting(TypenameHandling = TypenameHandling.All)] // <--- Something like this?
public Value TheValue { get; set; }
}
I am aware that I could specify this behavior when doing the parsing with a custom converter.
But I want to include the typeinformation every time objects of this type is serialized, without manually having to specify which serialization options to use.
Newtonsoft.Json's JsonPropertyAttribute has TypeNameHandling property which you can set:
public class Root
{
[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
public Base Prop { get; set; }
}
public class Base
{
public int IntProp { get; set; }
}
public class Child:Base
{
}
// Example:
var result = JsonConvert.SerializeObject(new Root
{
Prop = new Child()
});
Console.WriteLine(result); // prints {"Prop":{"$type":"SOAnswers.TestTypeNamehandling+Child, SOAnswers","IntProp":0}}

How to serialize derived class to base class?

I want to serialize object and pass it to method which parameter type is parent of object.
For example, I have this classes.
public class Base
{
public string TypeName => GetType().Name;
public string Data => JsonConvert.SerializeObject(this);
}
public class Derived : Base
{
public string Name { get; set; }
public int data1 { get; set; }
public int data2 { get; set; }
}
public class Derived2 : Base
{
...
}
....
I wrote the code as follows,
var obj = new Derived { Name = "John", data1 = 2000, data2 = 1500 };
Send(obj);
And Send(..) method is,
public void Send(Base info)
{
// Do Something with "info".
}
When I instantiate variable obj, program has fallen into infinite recursion because of "Data" in Base class.
How can I change the code?
Infinite recursion is caused by the Data property, which is serialized - that causes serialization of the this and the loop begins.
The best solution would be to simply change the property into method, which would not be serialized and would better serve the purpose. If you are dead set on property - you could just try marking the property with http://www.newtonsoft.com/json/help/html/PropertyJsonIgnore.htm which will cause it to be ignored during serialization.

C# ServiceStack JsonSerializer Deserialize

How can I deserialize a string to Json object where the json Object can be a single or an array, right now I have this, which works but its a hack (pseudo):
class MyObject{
public string prop1
public string prop2;
}
class MyList{
List<MyObject> objects {get; set; }
}
class Test{
MyList list = JsonSerialzer.Deserialize<MyList>(str);
//if list is null - it can be single
if(list == null){
MyObject myObject = JsonSerializer.Deserialize<MyObject>(str);
if(myObject != null)
list.add(myObject);
}
}
As shown above, problem is the json String I am receiving from another service can be either single or list. How to handle this elegantly?
I would strongly advise against accepting different structures in the same argument, it makes your software highly brittle and unpredictable. But if it could be a list you can just check the first char is a [, e.g:
if (str.TrimStart().StartsWith("["))
{
MyList list = JsonSerialzer.Deserialize<MyList>(str);
}
else
{
MyObject myObject = JsonSerializer.Deserialize<MyObject>(str);
}
Also please note that by default all ServiceStack text serializers only serialize public properties, so you need to add getters/setters to each property you want serialized, e.g:
class MyObject
{
public string prop1 { get; set; }
public string prop2 { get; set; }
}
class MyList
{
List<MyObject> objects { get; set; }
}
Otherwise you can configure ServiceStack.Text to also serialize public fields with:
JsConfig.IncludePublicFields = true;

C# JsonConvertDeserialization returning null values

I am trying to understand why I am getting null values for the following:
Json:
{
"IdentityService": {
"IdentityTtlInSeconds": "90",
"LookupDelayInMillis": "3000"
}
}
Class:
public class IdentityService
{
public string IdentityTtlInSeconds { get; set; }
public string LookupDelayInMillis { get; set; }
}
Called with :
_identityService = JsonConvert.DeserializeObject<IdentityService>(itemAsString);
The class is instantiated but the values for IdentityTtlInSeconds and LookupDelayInMillis are null. I cannot see why they should be
You need one more class - an object which has one property called IdentityService:
public class RootObject
{
public IdentityService IdentityService { get; set; }
}
You need this class because JSON that you have has one property called IdentityService, and this object has two properties, called IdentityTtlInSeconds and LookupDelayInMillis. If you are using a default serializer your classes need to reflect the structure that you have in your JSON string.
And now you can use it to deserialize your string:
var rootObject = JsonConvert.DeserializeObject<RootObject>(itemAsString);
_identityService = rootObject.IdentityService;

Categories