JsonConverter how to deserialize to generic object - c#

I'm sending this structure through webapi:
[DataContract]
public class PacketData
{
public enum Opcodes
{
Hello = 0x00,
Close = 0x01,
Serial = 0x02,
GPIO = 0x04
}
[DataMember]
public object Data { get; private set; }
[DataMember]
public Opcodes Opcode { get; private set; }
public PacketData(Opcodes opcode, object data)
{
Data = data;
Opcode = opcode;
}
}
And my problem is that I set on server side when I sending it I assign to Data few class ex. CustomClass1, CustomClass2
Now on deserialize I get instead of object string which is:
{\r\n \"Cmd\": 5,\r\n \"BaudRates\": [\r\n 2400,\r\n 4800,\r\n 9600,\r\n 19200,\r\n 38400,\r\n 57600,\r\n 115200\r\n ],\r\n \"SerialPorts\": null,\r\n \"IsOpen\": false,\r\n \"BaudRate\": 0,\r\n \"PortName\": null,\r\n \"WriteCMD\": null,\r\n \"WriteDATA\": null,\r\n \"Read\": null\r\n}
So Data is string instead of class or C# classic object type
And there is problem I don't know how to recognize from string if its CustomClass1 or CustomClass2.
Any ideas how to resolve this?
Thanks.
EDIT: Including deserialize and serialize
[HttpGet("Send", Name = "Send")]
public IActionResult Send()
{
return Ok(WebAPI.Send(), HttpStatusCode.OK);
}
public IEnumerable<string> Send()
{
List<string> packets = new List<string>();
foreach (PacketData packet in StaticConfig.SendStack.ToArray())
packets.Add(JsonConvert.SerializeObject(packet));
return packets.ToArray();
}
And this is deserialize:
string json = await client.GetStringAsync(new Uri("http://localhost:5000/api/Send"));
string[] jsonArray = JsonConvert.DeserializeObject<string[]>(json);
if (jsonArray.Length == 0)
Task.Delay(100).Wait();
List<PacketData> packets = new List<PacketData>();
foreach (string j in jsonArray)
packets.Add(JsonConvert.DeserializeObject<PacketData>(j));
foreach (PacketData packet in packets)
{
string p = packet.Data.ToString();
bool a = packet.Data is PacketSerialModel; // returns false
HandleReceivedData(this, new ReceivedDataArgs(packet));
}
EDIT 2:
So what do I want?
I would like to get back mentioned string into PacketData.Data then I can use something like this:
if(packet.Data is CustomClass1)
{
}
else if(packet.Data is CustomClass2)
{
var b = packetData as CustomClass2;
//...
}
currently my packet.Data is string and I need to create on this object properties and set values to them based on json.
EDIT3:
using now
JsonSerializerSettings()
{ TypeNameHandling = TypeNameHandling.Auto }
Works perfectly, but I have to replace in incoming json string project name
like in following string:
["{\"Data\":{\"$type\":\"Shared.PacketSerialModel, ASP_MVC\",\"Cmd\":5,\"BaudRates\":[2400,4800,9600,19200,38400,57600,115200],\"SerialPorts\":null,\"IsOpen\":false,\"BaudRate\":0,\"PortName\":null,\"WriteCMD\":null,\"WriteDATA\":null,\"Read\":null},\"Opcode\":2}"]
I have to replace ASP_MVC to second project name, any workaround than replace?

To answer your updated question, "how do I remap assembly names when exchanging JSON data containing $type information between different .Net assemblies using Json.NET", you have a few options:
The simplest would be to extract the types in question into a shared DLL and reference that DLL from both assemblies. This solves the problem and also reduces code duplication.
If you cannot do this, you will need to write your own SerializationBinder, probably inheriting from Json.NET's DefaultSerializationBinder, as is described in the documentation: Custom SerializationBinder. If your classes are not generic, can simply remap the assemblyName:
public class SimpleAssemblyMappingSerializationBinder : DefaultSerializationBinder
{
readonly string oldAssemblyName;
readonly string newAssemblyName;
public SimpleAssemblyMappingSerializationBinder(string oldAssemblyName, string newAssemblyName)
{
this.oldAssemblyName = oldAssemblyName;
this.newAssemblyName = newAssemblyName;
}
public override Type BindToType(string assemblyName, string typeName)
{
if (assemblyName == oldAssemblyName)
assemblyName = newAssemblyName;
return base.BindToType(assemblyName, typeName);
}
}
For a similar binder, see Handling namespace changes with TypeNameHandling.All.
But if your classes are generic (for instance, if Data is sometimes a List<CustomClass3>), you will need to parse the generic parameters inside the type name. For an idea of how to do this see How to create a SerializationBinder for the Binary Formatter that handles the moving of types from one assembly and namespace to another. That question is about BinaryFormatter but the answer applies to Json.NET also.
To omit the assembly name and namespace entirely, you could use the binder from Json serialization for Object Data Type for both serialization and deserialization.
Finally, you could consider switching to DataContractJsonSerializer which exchanges type information indirectly by using contract names rather than raw .Net type names. While the data contract serializer is generally less flexible than Json.NET, its contract-based exchange of type information may be more suitable in this case. See for instance How to deserialize JSON with unnamed collection of types using DataContractSerializer.

Related

How can I deserialize a json object into a complex object?

I'm trying to deserialize a JSON object into an Object with some "object" attribute that may be different each time. I have a serializer/deserializer function that works fine when using simple variable types or defined ones.
I tried to cast the object into the correct class, tried to get the object as dynamic, etc. However, I always get an exception: "Object Reference not established..."
Deserialization func:
public static T deserializeJSON<T>(string json)
{
T obj = Activator.CreateInstance<T>();
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType($
obj = (T)serializer.ReadObject(ms);
}
return obj;
}
Example object:
[DataContract]
class Client
{
[DataMember] private string name;
[DataMember] private string address;
[DataMember] private string bio;
[DataMember] private object specific; //WHERE SPECIFIC MAY BE ANY OTHER OBJECT THAT I CAST AFTER DESERIALIZATION
}
Example object2:
[DataContract]
class Server
{
[DataMember] private string name;
[DataMember] private int value;
}
The specific attribute may be any other object. Imagine "specific" attribute is a Server type object; The deserialization function loads all attributes well instead of specific that is loaded as an object, but cannot convert it to Server.
PD: At deserialization moment, I know what class type is the "specific" attribute.
Thanks!
using latest json.net you can use
dynamic data = Json.Decode(json);
or
dynamic data = JObject.Parse(json);
and can access data with following code :
data.Date;
data.Items.Count;
data.Items[0].Name;
data.Items[0].Price;
data.Items[1].Name;

C# Parse/Deserialize JSON partially with Newtonsoft

I have to extract a part of json-string using .net or newtonsoft json.
JSON:
var json = "{\"method\":\"subtract\",\"parameters\":{\"minuend\":\"SOME_CUSTOM_JSON_OBJECT_DIFFERENT_FOR_EACH_METHOD\",\"subtrahend\":23}}";
C# Class:
class MyJson{
public string method { get; set; }
//public string parameters {get; set;}
public object parameters {get; set;}
}
I do not need to parse all the children of "parameters" json-object. "parameters" could be a very big object ([{obj1}...{obj1000}], objX of 1000 fields), parse which would be not performant.
I would like i.e. to pass it exactly as it is on some point, so conversion "string-C#object-string" would be redundant.
I do not want use Regexp or string transformations (string.Substring, Split and co), because of error margin, I know that all .net and newtonsoft string transformations based.
Question 1: if I define a property of type "object", how newtonsoft will handle this? (Documentation is worse than msdn, so I'm looking for the input from you, who already tried this).
static void Main(string[] args)
{
var json = "{\"method\":\"subtract\",\"parameters\":{\"minuend\":42,\"subtrahend\":23}}";
var data = JsonConvert.DeserializeObject<MyJson>(j);
// what internal representaion of data.parameters?
// How is it actually converted from json-string to an C# object (JObject/JsonObject).
}
In perfect case:
"parameters" is a string and calling
ExtractMyJson(jsonString)
gives me the json string of parameters.
Basically I need the newtonsoft version of
string ExtractMyJson(jsonString){
var p1 = jsonString.Split(",");
// .. varios string transformations
return pParams;
}
Note: please don't reference "dynamic" keyword or ask why no string transformations, it's the very specific question.
If you know that your parameters are unique you can do something like this:
class MyJson
{
public string method { get; set; }
public Dictionary<string,object> parameters { get; set; }
}
................
string json = "{\"method\":\"subtract\",\"parameters\":{\"minuend\":{\"img\": 3, \"real\": 4},\"subtrahend\":23}}";
var data = JsonConvert.DeserializeObject<MyJson>(json);
If you let it as object is going to receive the type Newtonsoft.Json.Linq.JObject.
Have you tried JTOKEN?
It is a rather simple solution to partially read basic or nested JSONs as described in this post.
For a nested JSON
{
"key1": {
"key11": "value11",
"key12": "value12"
}
"key2": "value2"
}
it would look like this
JToken token = JToken.Parse(json);
var value12 = token.SelectToken("key1.key12");
to get the element of the key "key12.
I think this could go nicely with your problem.
Well Objects are treated the same way your parent object is treated. It will start from the base of the graph. So if you have something like:
Person
{
Address Address {get;set;}
}
The Json will start Deserializing Address and then add in the Person object.
If you want to limit thesize of the graph depth you can use a setting like :
JsonConvert.DeserializeObject<List<IList<IList<string>>>>(json, new JsonSerializerSettings
{
MaxDepth = 2
});
For more configurations of the JsonSerializer check JsonSerializerSettings
If your field is an object then that object will have the KeyValuePair of every property that it holds, based on that when you cast that field you can access that type.(the behaviour is the same as assigning a type to an object in C#).
Update: So if you question using JsonObject or type, well JObject is and intermediary way to construct the json format in a generic format. But using the Type deserializatin means you can ignore properties you are not interested in. Mapping to a json with a type makes more sense because it creates a new object and dismisses the old JObject.

Determining object "type" during json deserialization

While trying to de-serialize a complex JSON object (JIRA issue) into an object containing a dictionary of type string-Field I've hit a bit of a bump.
While I can de-serialize various pre-determined object types (standard), I'm having a bit of a harder time with the custom fields, which could be of various types (they all begin with customfield_ followed by a set of numbers).
The custom fields can be floats, strings, booleans, objects and arrays of objects. The latter of these is causing me issues since I can't seem to determine what the object is before I de-serialize it.
I've searched for a way to perhaps "peek" at the data in the object before de-serializing as one of the fields contains information specific to it's type. This is all so I can determine the type of the object and tell Json.Net what to de-serialize it as.
I've considered parsing the JSON string before serialization to get the information, or maybe just when hitting this particular case, but maybe there is a better way?
Thanks in advance for any advice on this.
You can deserialize to an object with Json.Net. Here's a quick and dirty example:
using System;
using Newtonsoft.Json;
namespace Sandbox
{
class Program
{
private static void Main(string[] args)
{
var nestDto = new Dto
{
customfield_1 = 20,
customfield_2 = "Test2"
};
var dto = new Dto
{
customfield_1 = 10,
customfield_3 = new[] { nestDto },
customfield_2 = "Test"
};
var jsonString = JsonConvert.SerializeObject(dto);
Console.WriteLine(jsonString);
var fromJsonString = JsonConvert.DeserializeObject<Dto>(jsonString);
Console.WriteLine(fromJsonString.customfield_3[0].customfield_2); //Outputs Test2
Console.ReadKey();
}
}
class Dto
{
public int customfield_1 { get; set; }
public string customfield_2 { get; set; }
public Dto[] customfield_3 { get; set; }
}
}
Instead of peaking, you can deserialize as the same type as JSON.net uses for ExtensionData explicitly. For example:
if (reader.TokenType == JsonToken.StartArray)
{
var values = serializer.Deserialize<List<Dictionary<string, JToken>>>(reader);
objectContainer = ClassifyAndReturn(values);
}
private ObjectType ClassifyAndReturn(List<Dictionary<string, JToken>> values)
{
if (values.First().ContainsKey("self"))
{
string self = values.First()["self"].Value<string>();
if (self.Contains("customFieldOption"))
//... Then go into a series of if else cases to determine the object.
The representation of the objects are given as a Dictionary of string to JToken, which can then easily be checked and assigned manually or in some cases automatically deserialized (in the case one of the fields is another object).
Here is what an object constructor could look like:
internal myobject(Dictionary<string, JToken> source)
{
Self = source["self"].Value<string>();
Id = source["id"].Value<string>();
Value = source["value"].Value<string>();
}

How to map a data contract to refer to correct fields in a JSON object?

I'm recieving a JSON object that looks like the example below.
{
"name1":{"name1a":"value1a","name1b":"value1b"},
"name2":{"name2a":"value2a","name2b":"value2b"}
}
I've set up a data contract for it (since I only need to access a single data field at the moment) like this.
[DataContract]
public class MyThingy
{
[DataMember(Name="name1b")]
public string Name1b { get; set; }
public MyThingy() { }
public MyThingy(String name1b)
{
Name1b = name1b;
}
}
When I've serialized the object, I try to print it out (which works, since I'm getting a string description of the class) and them the field Name1b. The last part doesn't work and I'm getting null there. My guess is that I must have mapped the data contract wrongly but I can't see how to correct it.
How should the MyThingy class be declared?
My JSON object is fetched as described in this post.
I would use JavaScriptSerializer here,
string json = #"{
""name1"":{""name1a"":""value1a"",""name1b"":""value1b""},
""name2"":{""name2a"":""value2a"",""name2b"":""value2b""}
}";
var obj = new JavaScriptSerializer()
.Deserialize<Dictionary<string, Dictionary<string, string>>>(json);
Console.WriteLine(obj["name1"]["name1b"]);
You can also use Json.Net and dynamic together
dynamic obj = JsonConvert.DeserializeObject(json);
Console.WriteLine(obj.name1.name1b);

Deserializing a mixed list of objects from JSON

I'm using the DataContractJsonSerializer to deserialize objects from an external service. In most cases, this has worked great for me. However, there is one case where I need to deserialize JSON that contains a list of objects that all inherit from the same base class, but there are many different types of objects in that list.
I know that it can be done easily by including a list of known types in the serializer's constructor, but I don't have access to the code that generated this JSON service. The types that I'm using will be different from the types used in the service (mostly just the class name and namespace will be different). In other words, the classes that the data was serialized with will not be the same classes that I'll use to deserialize it even though they'll be very similar.
With the XML DataContractSerializer, I can pass in a DataContractResolver to map the services types to my own types, but there is no such constructor for the DataContractJsonSerializer. Is there any way to do this? The only options that I've been able to find are: write my own deserializer, or use Microsoft's JsonObject that isn't tested and "should not be used in production environments."
Here is an example:
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Student : Person
{
[DataMember]
public int StudentId { get; set; }
}
class Program
{
static void Main(string[] args)
{
var jsonStr = "[{\"__type\":\"Student:#UnknownProject\",\"Name\":\"John Smith\",\"StudentId\":1},{\"Name\":\"James Adams\"}]";
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
writer.Write(jsonStr);
writer.Flush();
stream.Position = 0;
var s = new DataContractJsonSerializer(typeof(List<Person>), new Type[] { typeof(Student), typeof(Person) });
// Crashes on this line with the error below
var personList = (List<Person>)s.ReadObject(stream);
}
}
}
Here is the error mentioned in the comment above:
Element ':item' contains data from a type that maps to the name
'http://schemas.datacontract.org/2004/07/UnknownProject:Student'. The
deserializer has no knowledge of any type that maps to this name. Consider using
a DataContractResolver or add the type corresponding to 'Student' to the list of
known types - for example, by using the KnownTypeAttribute attribute or by adding
it to the list of known types passed to DataContractSerializer.
I found the answer. It was very simple. I just needed to update my DataContract attribute to specify which namespace (you can also specify a different name) they map to in the source JSON like this:
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject")]
public class Person
{
[DataMember]
public string Name { get; set; }
}
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject"]
public class Student : Person
{
[DataMember]
public int StudentId { get; set; }
}
That JsonObject was a sample for .NET 3.5. There is a project in codeplex - http://wcf.codeplex.com - which has a tested implementation of the JsonValue/JsonObject/JsonArray/JsonPrimitive classes, including source code and unit tests. With that you can parse "untyped" JSON. Another well-used JSON framework is the JSON.NET at http://json.codeplex.com.
You may create a DTO before serializing.
I use a class like: (pseudo code)
class JsonDto
string Content {get;set;}
string Type {get;set;}
ctor(object) => sets Content & Type Properties
static JsonDto FromJson(string) // => Reads a Serialized JsonDto
// and sets Content+Type Properties
string ToJson() // => serializes itself into a json string
object Deserialize() // => deserializes the wrapped object to its saved Type
// using Content+Type properties
T Deserialize<T>() // deserializes the object as above and tries to cast to T
Using the JsonDto you can easily serialize arbitrary objects to JSON and deserialize them to their common base type because the deserializer will always know the original type and returns an type of object reference which will be casted if you use the generic Deserialize<T> method.
One caveat: If you set the Type property you should use the AssemblyQualifiedName of the type, however without the version attribute (ex: MyCompany.SomeNamespace.MyType, MyCompany.SomeAssembly). If you just use the AssemblyQualifiedName property of the Type class you will end up with errors if your assembly version changes.
I implemented a JsonDtoCollection the same way, which derives from List<JsonDto> and provides methods to handle collections of objects.
class JsonDtoCollection : List<JsonDto>
ctor(List<T>) => wraps all items of the list and adds them to itself
static JsonDtoCollection FromJson(string) // => Reads a collection of serialized
// JsonDtos and deserializes them,
// returning a Collection
string ToJson() // => serializes itself into a json string
List<object> Deserialize() // => deserializes the wrapped objects using
// JsonDto.Deserialize
List<T> Deserialize<T>() // deserializes the as above and tries to cast to T

Categories