I'm writing a service to retrieve data from an API over HTTP from another team in my company. The JSON response body from their API looks a bit like this:
"SomeObject": {
"SomeInnerObject": {
"SomeProperty": {
"Id": "123",
"Type": "abc",
"Name": "some value"
}
}
}
I'm writing a C# class to store the data in memory in order to do some comparisons. The nesting of the JSON object causes the class to look annoyingly repetitive:
public class MyClass
{
public SomeObjectModel SomeObject { get; set; }
public class SomeObjectModel
{
public SomeInnerObjectModel InnerObject { get; set; }
public class SomeInnerObjectModel
{
// etc...
}
}
}
I know for sure that the inner classes, like "SomeObjectModel", are only going to be read from and not instantiated elsewhere, so is there a way to combine the class definition and property definition lines into something more like this?
public class MyClass
{
public SomeObject { get; set; } :
{
public SomeInnerObject { get; set; } :
{
// etc...
}
}
}
EDIT:
The JSON will have arrays in it, so take that into account if you are proposing an alternative using generics, etc.
If you're using this just to deserialize the JSON, you don't even need to define a class.. You can use a nested anonymous type.
First, create and populate (with dummy values) an anonymous type that has the properties you need to be able to read. Then pass it as the second parameter to DeserializeAnonymousType<T>(string,T).
The result is a new instance of the same anonymous type that you created, but now it's populated with values from the JSON.
var json = #"{'SomeObject': {'SomeInnerObject': {'SomeProperty': {'Id': '123','Type': 'abc','Name': 'some value'}}}}";
var template = new
{
SomeObject = new
{
SomeInnerObject = new
{
SomeProperty = new
{
Id = default(int),
Type = default(string),
Name = default(string)
}
}
}
};
var result = JsonConvert.DeserializeAnonymousType(json, template);
var id = result.SomeObject.SomeInnerObject.SomeProperty.Id;
var type = result.SomeObject.SomeInnerObject.SomeProperty.Type;
var name = result.SomeObject.SomeInnerObject.SomeProperty.Name;
Console.WriteLine("{0} {1} {2}", id, type, name);
Output:
123 abc some value
See my working example on DotNetFiddle.
Edit: If your JSON contains an array, you can use new[] {} to create an array based on type inference and then put the anonymous types inside, like this:
var json = #"{ 'SomeObjects': [ { 'Id': '123', 'Name': 'some value' }, { 'Id': '456', 'Name': 'another value' } ]}";
var template = new
{
SomeObjects = new [] { new { Id=default(int), Name=default(string)} }
};
The short answer is no, C# does not support any version of that syntactic sugar. The closest thing in C# is probably either anonymous types or value tuples.
Other languages have similar features:
C++ supports "inline classes" in declarations.
Java supports anonymous classes that are more sophisticated than C# anonymous record types.
Scala supports case classes which declare their members in the header of the declaration.
And so on. I think the first is the closest thing to what you are looking for.
Scala-style class declarations have been proposed for C# many times in the last decade, and may finally make it into C# 8; see https://blog.cdemi.io/whats-coming-in-c-8-0-records/
You can use the dynamic type. Here is a minimal example:
using Newtonsoft.Json;
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
dynamic x = JsonConvert.DeserializeObject("[{key: '1001', value: 'test'}, {key: '1002', value: 'test2'}, ]");
Console.WriteLine(x[0].key);
Console.WriteLine(x[0].value);
Console.WriteLine(x[1].key);
Console.WriteLine(x[1].value);
Console.ReadLine();
}
}
}
You can create classes by using the paste special, paste JSON as classes.
https://channel9.msdn.com/Series/Windows-Store-Developer-Solutions/Quckly-Generate-C-Classes-from-JSON-Responses#time=01m56s
Related
I need to deserialize just part of a JSON string returned from a server. The 'myData' portion in the JSON string below.
My JSON string is structured as follows.
{
"data": {
"CODE": {
"someData": {
"h": "foo",
"id": "City",
"lat": "11.11111"
},
"feedMe": [
[
{
"myData": {
"item1": "a",
"item2": "b",
"item3": "c"
},
"moreData": {}
}
]
]
}
}
}
In Unity there is the JSONutility.FromJson method
https://docs.unity3d.com/ScriptReference/JsonUtility.FromJson.html
but unsure how I would either
1 pass only the 'myData' portion to this method.
or
2 Deserialize the entire string
An alternativ to using JsonUtility there is good old SimpleJSON which allows you to only access a certain field of your json like e.g.
var N = JSON.Parse(the_JSON_string);
var myData = N["data"]["CODE"]["feedMe"][0][0];
var item2 = myData["item2"].Value;
In general the simplest way to get the needed c# class structure for your json is always using json2csharp and make all classes [Serializable] and remove the {get; set;} in order to use fields instead of properties. Something like this
[Serializable]
public class SomeData
{
public string h;
public string id;
public string lat;
}
[Serializable]
public class CODE
{
public SomeData someData;
public List<List<MyData>> feedMe;
}
[Serializable]
public class MyData
{
public string item1;
public string item2;
public string item3;
}
[Serializable]
public class Data
{
public CODE CODE;
}
[Serializable]
public class RootObject
{
public Data data;
}
Instead of List<T> you can also use T[] if you like. And the class names actually don't matter but the structure and field names have to match.
and then use
var root = JsonUtility.FromJson<RootObject>(THE_JSON_STRING);
var myData = root.data.CODE.feedMe[0][0];
var item2 = myData.item2;
As already comented however there is a nested array in your array .. not sure if this is intended.
well, use one of the powerful json nuget -newtonsoft.json , then in your code you can iterate the values like below
var files = JObject.Parse(YourJSON);
var recList = files.SelectTokens("$..data").ToList();
foreach (JObject obj in recList.Children())
{
foreach (JProperty prop in obj.Children())
{
var key = prop.Name.ToString();
var value = prop.Value.ToString();
//Do your stuffs here
}
}
JsonUtility not work whit json files, this only for save and load basic public variables of some class. Asset Store have many frameworks for parse json. p.s. your json is strange, [] its array and you have feedMe:[[{myData, moreData}]]. One array whene just one object in array... parse confusing.
I have a converter class that receives json in input, here are 2 valid examples:
{
"method": "Model",
"payload": {
"key": "value"
}
}
and
{
"method": "OtherModel",
"payload": {
"foo": "bar"
}
}
In C#, I have classes mapped to each possible model:
public class Model
{
public string Key { get; set; }
}
public class OtherModel
{
public string Foo { get; set; }
}
I need a generic converter
How can I use the string value in the method of the JSON to convert in a generic way the content of the payload field?
Is using a huge switch the only way? This is the prototype I have so far but there are hundreds of different models so it will grow quite large...
public IResult ParseJson(string json)
{
Regex regexMessageName = new Regex("\"messageName\": \"(.*?)\"", RegexOptions.Compiled);
var messageName = regexMessageName.Match(json).Groups[1].Value;
switch (messageName)
{
case "Model":
var raw = JsonConvert.DeserializeObject<JsonData<Model>>(json);
return new LogInfoRequestResult<Model> { Raw = raw };
case "OtherModel":
var raw = JsonConvert.DeserializeObject<JsonData<OtherModel>>(json);
return new LogInfoRequestResult<OtherModel> { Raw = raw };
}
}
If you want complete control of your classes, and allow them to evolve independently, then you can have one base class that owns the Method, and then as many subclasses as you want with their own definition of the payload.
First, parse into the baseclass, just to get a strongly typed deserialization of Method
Then, there are a lot of patterns to address branching logic.
If you have 1-2 cases, an if statement is fine
If you have 3-5 cases, you can use a switch
If you have 6-10 cases, you can create a dictionary that maps method name to class type
If you have more than that, you can use the strategy pattern and pass an interface around
Here's an example of how you could write the code:
var json = #"{
'method': 'Model',
'payload': {
'key': 'value'
}
}";
var modelBase = JsonConvert.DeserializeObject<ModelBase>(json);
var methodMapping = new Dictionary<string, Type>()
{
{MethodTypes.Model.ToString(), typeof(Model)},
{MethodTypes.OtherModel.ToString(), typeof(OtherModel)},
};
Type methodClass = methodMapping[modelBase.Method];
var result = JsonConvert.DeserializeObject(json, methodClass);
Note: Since we're programmatically determining the correct type, it's hard to pass to a generic <T>, so this uses the overload of DeserializeObject that takes type as a param
And here are the classes that model incoming messages
public enum MethodTypes
{
Model,
OtherModel
}
public class ModelBase
{
public string Method { get; set; }
}
public class Model : ModelBase
{
public ModelInfo Payload { get; set; }
public class ModelInfo
{
public string Key { get; set; }
}
}
public class OtherModel : ModelBase
{
public ModelInfo Payload { get; set; }
public class ModelInfo
{
public string Foo { get; set; }
}
}
Dictionary<string,string>
If your data is always going to be "foo":"bar" or "key":"value" .... string:string, then Cid's suggesting to use Dictionary<string,string> Payload makes a lot of sense. Then figure out however you want to map from that c# class in a c# constructor that returns whatever type you want.
Additional Resources:
How to handle both a single item and an array for the same property using JSON.net
Deserializing polymorphic json classes without type information using json.net
JSON.NET - Conditional Type Deserialization
Conditionally deserialize JSON string or array property to C# object using JSON.NET?
You can instanciate an object of the expected class using Activator.CreateInstance(), then populate it with JsonConvert.PopulateObject()
In example :
Type t = Type.GetType($"NameSpaceName.{messageName}"); // this must be a fully qualified name
object obj = Activator.CreateInstance(t);
JsonConvert.PopulateObject(json, obj);
We are using ASP.NET Web API 2 and want to expose ability to partially edit some object in the following fashion:
HTTP PATCH /customers/1
{
"firstName": "John",
"lastName": null
}
... to set firstName to "John" and lastName to null.
HTTP PATCH /customers/1
{
"firstName": "John"
}
... in order just to update firstName to "John" and do not touch lastName at all. Suppose we have a lot of properties that we want to update with such semantic.
This is quite convenient behavior that is exercised by OData for instance.
The problem is that default JSON serializer will just come up with null in both cases, so it's impossible to distinguish.
I'm looking for some way to annotate model with some kind of wrappers (with value and flag set/unset inside) that would allow to see this difference. Any existing solutions for this?
I know that answers which are already given cover all aspects already, but just want to share concise summary of what we ended up doing and what seems to work for us pretty well.
Created a generic data contract
[DataContract]
public class RQFieldPatch<T>
{
[DataMember(Name = "value")]
public T Value { get; set; }
}
Created ad-hoc data cotnracts for patch requests
Sample is below.
[DataContract]
public class PatchSomethingRequest
{
[DataMember(Name = "prop1")]
public RQFieldPatch<EnumTypeHere> Prop1 { get; set; }
[DataMember(Name = "prop2")]
public RQFieldPatch<ComplexTypeContractHere> Prop2 { get; set; }
[DataMember(Name = "prop3")]
public RQFieldPatch<string> Prop3 { get; set; }
[DataMember(Name = "prop4")]
public RQFieldPatch<int> Prop4 { get; set; }
[DataMember(Name = "prop5")]
public RQFieldPatch<int?> Prop5 { get; set; }
}
Business Logic
Simple.
if (request.Prop1 != null)
{
// update code for Prop1, the value is stored in request.Prop1.Value
}
Json format
Simple. Not that extensive as "JSON Patch" standard, but covers all our needs.
{
"prop1": null, // will be skipped
// "prop2": null // skipped props also skipped as they will get default (null) value
"prop3": { "value": "test" } // value update requested
}
Properties
Simple contracts, simple logic
No serialization customization
Support for null values assignment
Covers any types: value, reference, complex custom types, whatever
At first I misunderstood the problem. As I was working with Xml I thought it was quite easy. Just add an attribute to the property and leave the property empty. But as I found out, Json doesn't work like that. Since I was looking for a solution that works for both xml and json, you'll find xml references in this answer. Another thing, I wrote this with a C# client in mind.
The first step is to create two classes for serialization.
public class ChangeType
{
[JsonProperty("#text")]
[XmlText]
public string Text { get; set; }
}
public class GenericChangeType<T> : ChangeType
{
}
I've chosen for a generic and a non-generic class because it is hard to cast to a generic type while this is not important. Also, for xml implementation it is necessary that XmlText is string.
XmlText is the actual value of the property. The advantage is that you can add attributes to this object and the fact that this is an object, not just string. In Xml it looks like: <Firstname>John</Firstname>
For Json this doesn't work. Json doesn't know attributes. So for Json this is just a class with properties. To implement the idea of the xml value (I will get to that later), I've renamed the property to #text. This is just a convention.
As XmlText is string (and we want to serialize to string), this is fine to store the value disregard the type. But in case of serialization, I want to know the actual type.
The drawback is that the viewmodel needs to reference these types, the advantage is that the properties are strongly typed for serialization:
public class CustomerViewModel
{
public GenericChangeType<int> Id { get; set; }
public ChangeType Firstname { get; set; }
public ChangeType Lastname { get; set; }
public ChangeType Reference { get; set; }
}
Suppose I set the values:
var customerViewModel = new CustomerViewModel
{
// Where int needs to be saved as string.
Id = new GenericeChangeType<int> { Text = "12" },
Firstname = new ChangeType { Text = "John" },
Lastname = new ChangeType { },
Reference = null // May also be omitted.
}
In xml this will look like:
<CustomerViewModel>
<Id>12</Id>
<Firstname>John</Firstname>
<Lastname />
</CustomerViewModel>
Which is enough for the server to detect the changes. But with json it will generate the following:
{
"id": { "#text": "12" },
"firstname": { "#text": "John" },
"lastname": { "#text": null }
}
It can work, because in my implementation the receiving viewmodel has the same definition. But since you are talking about serialization only and in case you use another implementation you would want:
{
"id": 12,
"firstname": "John",
"lastname": null
}
That is where we need to add a custom json converter to produce this result. The relevant code is in WriteJson, assuming you would add this converter to the serializer settings only. But for the sake of completeness I've added the readJson code as well.
public class ChangeTypeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// This is important, we can use this converter for ChangeType only
return typeof(ChangeType).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var value = JToken.Load(reader);
// Types match, it can be deserialized without problems.
if (value.Type == JTokenType.Object)
return JsonConvert.DeserializeObject(value.ToString(), objectType);
// Convert to ChangeType and set the value, if not null:
var t = (ChangeType)Activator.CreateInstance(objectType);
if (value.Type != JTokenType.Null)
t.Text = value.ToString();
return t;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var d = value.GetType();
if (typeof(ChangeType).IsAssignableFrom(d))
{
var changeObject = (ChangeType)value;
// e.g. GenericChangeType<int>
if (value.GetType().IsGenericType)
{
try
{
// type - int
var type = value.GetType().GetGenericArguments()[0];
var c = Convert.ChangeType(changeObject.Text, type);
// write the int value
writer.WriteValue(c);
}
catch
{
// Ignore the exception, just write null.
writer.WriteNull();
}
}
else
{
// ChangeType object. Write the inner string (like xmlText value)
writer.WriteValue(changeObject.Text);
}
// Done writing.
return;
}
// Another object that is derived from ChangeType.
// Do not add the current converter here because this will result in a loop.
var s = new JsonSerializer
{
NullValueHandling = serializer.NullValueHandling,
DefaultValueHandling = serializer.DefaultValueHandling,
ContractResolver = serializer.ContractResolver
};
JToken.FromObject(value, s).WriteTo(writer);
}
}
At first I tried to add the converter to the class: [JsonConverter(ChangeTypeConverter)]. But the problem is that the converter will be used at all times, which creates a reference loop (as also mentioned in the comment in the code above). Also you may want to use this converter for serialization only. That is why I've added it to the serializer only:
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
Converters = new List<JsonConverter> { new ChangeTypeConverter() },
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
var s = JsonConvert.SerializeObject(customerViewModel, serializerSettings);
This will generate the json I was looking for and should be enough to let the server detect the changes.
-- update --
As this answer focusses on serialization, the most important thing is that lastname is part of the serialization string. It then depends on the receiving party how to deserialize the string into an object again.
Serialization and deserialization use different settings. In order to deserialize again you can use:
var deserializerSettings = new JsonSerializerSettings
{
//NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
Converters = new List<JsonConverter> { new Converters.NoChangeTypeConverter() },
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
var obj = JsonConvert.DeserializeObject<CustomerViewModel>(s, deserializerSettings);
If you use the same classes for deserialization then Request.Lastname should be of ChangeType, with Text = null.
I'm not sure why removing the NullValueHandling from the deserialization settings causes problems in your case. But you can overcome this by writing an empty object as value instead of null. In the converter the current ReadJson can already handle this. But in WriteJson there has to be a modification. Instead of writer.WriteValue(changeObject.Text); you need something like:
if (changeObject.Text == null)
JToken.FromObject(new ChangeType(), s).WriteTo(writer);
else
writer.WriteValue(changeObject.Text);
This would result in:
{
"id": 12,
"firstname": "John",
"lastname": {}
}
Here's my quick and inexpensive solution...
public static ObjectType Patch<ObjectType>(ObjectType source, JObject document)
where ObjectType : class
{
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
try
{
String currentEntry = JsonConvert.SerializeObject(source, settings);
JObject currentObj = JObject.Parse(currentEntry);
foreach (KeyValuePair<String, JToken> property in document)
{
currentObj[property.Key] = property.Value;
}
String updatedObj = currentObj.ToString();
return JsonConvert.DeserializeObject<ObjectType>(updatedObj);
}
catch (Exception ex)
{
throw ex;
}
}
When fetching the request body from your PATCH based method, make sure to take the argument as a type such as JObject. JObject during iteration returns a KeyValuePair struct which inherently simplifies the modification process. This allows you to get your request body content without receiving a deserialized result of your desired type.
This is beneficial due to the fact that you don't need any additional verification for nullified properties. If you want your values to be nullified that also works because the Patch<ObjectType>() method only loops through properties given in the partial JSON document.
With the Patch<ObjectType>() method, you only need to pass your source or target instance, and the partial JSON document that will update your object. This method will apply camelCase based contract resolver to prevent incompatible and inaccurate property names from being made. This method will then serialize your passed instance of a certain type and turned into a JObject.
The method then replaces all properties from the new JSON document to the current and serialized document without any unnecessary if statements.
The method stringifies the current document which now is modified, and deserializes the modified JSON document to your desired generic type.
If an exception occur, the method will simply throw it. Yes, it is rather unspecific but you are the programmer, you need to know what to expect...
This can all be done on a single and simple syntax with the following:
Entity entity = AtomicModifier.Patch<Entity>(entity, partialDocument);
This is what the operation would normally look like:
// Partial JSON document (originates from controller).
JObject newData = new { role = 9001 };
// Current entity from EF persistence medium.
User user = await context.Users.FindAsync(id);
// Output:
//
// Username : engineer-186f
// Role : 1
//
Debug.WriteLine($"Username : {0}", user.Username);
Debug.WriteLine($"Role : {0}", user.Role);
// Partially updated entity.
user = AtomicModifier.Patch<User>(user, newData);
// Output:
//
// Username : engineer-186f
// Role : 9001
//
Debug.WriteLine($"Username : {0}", user.Username);
Debug.WriteLine($"Role : {0}", user.Role);
// Setting the new values to the context.
context.Entry(user).State = EntityState.Modified;
This method will work well if you can correctly map your two documents with the camelCase contract resolver.
Enjoy...
Update
I updated the Patch<T>() method with the following code...
public static T PatchObject<T>(T source, JObject document) where T : class
{
Type type = typeof(T);
IDictionary<String, Object> dict =
type
.GetProperties()
.ToDictionary(e => e.Name, e => e.GetValue(source));
string json = document.ToString();
var patchedObject = JsonConvert.DeserializeObject<T>(json);
foreach (KeyValuePair<String, Object> pair in dict)
{
foreach (KeyValuePair<String, JToken> node in document)
{
string propertyName = char.ToUpper(node.Key[0]) +
node.Key.Substring(1);
if (propertyName == pair.Key)
{
PropertyInfo property = type.GetProperty(propertyName);
property.SetValue(source, property.GetValue(patchedObject));
break;
}
}
}
return source;
}
I know I'm a little bit late on this answer, but I think I have a solution that doesn't require having to change serialization and also doesn't include reflection (This article refers you to a JsonPatch library that someone wrote that uses reflection).
Basically create a generic class representing a property that could be patched
public class PatchProperty<T> where T : class
{
public bool Include { get; set; }
public T Value { get; set; }
}
And then create models representing the objects that you want to patch where each of the properties is a PatchProperty
public class CustomerPatchModel
{
public PatchProperty<string> FirstName { get; set; }
public PatchProperty<string> LastName { get; set; }
public PatchProperty<int> IntProperty { get; set; }
}
Then your WebApi method would look like
public void PatchCustomer(CustomerPatchModel customerPatchModel)
{
if (customerPatchModel.FirstName?.Include == true)
{
// update first name
string firstName = customerPatchModel.FirstName.Value;
}
if (customerPatchModel.LastName?.Include == true)
{
// update last name
string lastName = customerPatchModel.LastName.Value;
}
if (customerPatchModel.IntProperty?.Include == true)
{
// update int property
int intProperty = customerPatchModel.IntProperty.Value;
}
}
And you could send a request with some Json that looks like
{
"LastName": { "Include": true, "Value": null },
"OtherProperty": { "Include": true, "Value": 7 }
}
Then we would know to ignore FirstName but still set the other properties to null and 7 respectively.
Note that I haven't tested this and I'm not 100% sure it would work. It would basically rely on .NET's ability to serialize the generic PatchProperty. But since the properties on the model specify the type of the generic T, I would think it would be able to. Also since we have "where T : class" on the PatchProperty declaration, the Value should be nullable. I'd be interested to know if this actually works though. Worst case you could implement a StringPatchProperty, IntPatchProperty, etc. for all your property types.
I've readed others posts here about this question
Serializing a list of Object using Json.NET
Serializing a list to JSON
Merge two objects during serialization using json.net?
All very useful. Certain, I can serialize in one json two lists, but I cant deserialize it.
I´m working with Json Newtonsoft, C#, MVC5, framework 4.5. This is the scenario:
C# CODE
public class User
{
public int id { get; set; }
public string name { get; set; }
}
public class Request
{
public int id { get; set; }
public int idUSer{ get; set; }
}
List<User> UserList = new List<User>();
List<Request> RequestList = new List<Request>();
string json= JsonConvert.SerializeObject(new { UserList, RequestList });
JSON RESULT
{
"UserList":[
{
"id":1,
"name":"User 1"
},
{
"id":2,
"name":"User 2"
},
{
"id":3,
"name":"User 3"
}
],
"RequestList":[
{
"id":1,
"idUSer":1
},
{
"id":2,
"idUSer":1
},
{
"id":3,
"idUSer":1
},
{
"id":4,
"idUSer":2
}
]
}
C# DESERIALIZE
I dont Know how configure the settings of Json.Deserialize< ?, Settings>(json) for indicate what types of objects are being deserialized.
Change of approach
So that, change of approach, I've created a new class "Cover" in order to put the lists together and serialize one object
public class Cover
{
private List<User> user = new List<User>();
private List<Request> request = new List<Request>();
public List<User> User
{
get { return user;}
set { User = value;}
}
public List<Request> Request
{
get {return request;}
set {Request = value;}
}
}
SERIALIZE
string json = JsonConvert.SerializeObject(cover);
JSON The json result is the same.
DESERIALIZE
Cover result = JsonConvert.DeserializeObject<Cover>(json, new
JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
It's work fine. My situation is resolved but I have doubts about concepts, in my mind something is not clear:
MY QUESTIONS ARE:
For the first aproach:
Do you think that there is a way to deserialize a json with different lists of objects? Is not a good practice?
Second aproach: Why jsons are equals for first situation?
In JSON.NET you need to specify the type you're about to deserialize, by supplying its name as a type argument to DeserializeObject.
However, in this line:
string json= JsonConvert.SerializeObject(new { UserList, RequestList });
you create anonymous object and then serialize it - new { UserList, RequestList }. So there is the catch - you cannot use anonymous type as type arguments.
To handle such situations, JSON.NET provides DeserializeAnonymousType<>. It doesn't require you to supply the type argument; actually you can't as you going to deserialize anonymous type. Instead it is inferred from the type of the second argument, passed to the method. So you just create a dummy, anonymous object, without data and pass it to this method.
// jsonData contains previously serialized List<User> and List<Request>
void DeserializeUserAndRequest(string jsonData)
{
var deserializedLists = new {
UserList = new List<User>(),
RequestList = new List<Request>()
};
deserializedLists = JsonConvert.DeserializeAnonymousType(jsonData, deserializedLists);
// Do your stuff here by accessing
// deserializedLists.UserList and deserializedLists.RequestLists
}
Of course this all works fine, but this approach suggests that you already know the structure of the serialized data. If this structure doesn't match the structure of the initialized by you anonymous type you'll get nothing after the DeserializeAnonymousType method. And this is valid not just for the type of the properties of the anonymous type, but for their names too.
To your first question:
I would consider the option with the Cover class the 'best practice' as you are using the same model for serialization and deserialization and it's all up to Json.NET to figure out how to do the (de)serialization magic.
If for some reason you don't want to use this approach, there are two other options:
Anonymous types: http://www.newtonsoft.com/json/help/html/DeserializeAnonymousType.htm
Deserializing into a dictionary: http://www.newtonsoft.com/json/help/html/DeserializeDictionary.htm
To your second question - Are you sure the generated JSON is absolutely the same with both approaches? (You can use a tool like www.diffchecker .com to verify)
With your second approach the top-level names should be different - it should be 'Users' instead of 'UserList' and 'Requests' instead of 'RequestList'
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>();
}