Could JsonDocument be used as ContractResolver? - c#

I have a question regarding the newly released .NET Core 3.0 and its new System.Text.Json, I would like to ask if the new JsonDocument could be used similarly to ContractResolver class in Newtonsoft JSON.NET.
What I need is quite straightforward, assume one class with two properties
public string Name {get; set;}
public string Street {get; set;}
and I need to deserialize two different JSONs into this class.
JSON 1:
{"person_A":{"full-name":"Micheal","address-street":"1st avenue","address-city":"New York"},
"person_B":{"full-name":"John","address-street":"Green street","address-city":"London"}}
JSON 2:
{"man_A":{"fullname":"Bush","street":"1st avenue","city":"Washington","state":"US"},
"woman_B":{"fullname":"McCarter","street":"Green street","city":"London","state":"GB"}}
I need to deserialize property full-name, fullname as Name and address-street,street as Street. The other JSON fields are not needed.
To deserialize multiple JSON with different property names I am using a ContractResolver in JSON.NET, which works quite well.
The reason why I am looking into the JsonDocument is that I am deserializing just few JSON properties and with current approach I have to cache entire JSON. If I uderstood correctly, the JsonDocument should allow me to access just the properties I need.
Thanks!
EDIT
To clarify a bit what I am trying to ask here - the idea is to get info about the Json object but not to load it entirely since it is not needed, that's why I would like to use the new JsonDocument, select just the things that I need and load those. Since the Json property names are different than my class properties and I have multiple Json trying to deserialize into the same class, I need something like IContractResolver to match the class properties with the names in Json.
I was able to make something like this working, but I admit it is not very pretty.
private readonly Dictionary propertyMappings = new Dictionary();
private void AddMap<U>(Expression<Func<Instrument,U>> expression, string jsonPropertyName)
{
var memberExpression = (MemberExpression)expression.Body;
propertyMappings.Add(memberExpression.Member.Name, jsonPropertyName);
}
public override async Task<List<Person>> GetPeopleAsync(HttpRequestMessage request)
{
AddMap(x => x.Name, "full-name");
AddMap(x => x.Street, "address-street");
var result = new List<Person>();
var response = await SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var stream = await response.Content.ReadAsStreamAsync();
var document = await JsonDocument.ParseAsync(stream);
document.RootElement.GetProperty("people").EnumerateObject()
.ToList()
.ForEach(x =>
{
var person= new Person();
foreach (var p in propertyMappings)
{
x.Value.TryGetProperty(p.Value, out var prop);
var v = person.GetType().GetProperty(p.Key);
v.SetValue(person,Convert.ChangeType(prop.ToString(),v.PropertyType));
}
result.Add(person);
});
return result;
}</code>

Related

How to get values from dynamic array?

I have deserialized the string by using the dynamic object.
Because property names dynamically changed. so can't able to deserialize with DTO objects.
Please find the code snippet:
var result = "[{\"series1\":\"{\\\"Category1\\\":4.3,\\\"Category2\\\":2.5,\\\"Category3\\\":3.5}\"},{\"series2\":\"{\\\"Category1\\\":2.4,\\\"Category2\\\":4.4,\\\"Category3\\\":1.8}\"},{\"series3\":\"{\\\"Category1\\\":2,\\\"Category2\\\":2,\\\"Category3\\\":3}\"}]");
var jsonResult = JsonConvert.DeserializeObject<dynamic[]>(jsonText);
How to retrieve the series1 and category1, 2, 3 results?
Could you please help me to solve this?
The problem you will have is you have json stuffed within json - obvious from the fact that you have double escape sequences there. So to get series1 is fairly straightforward:
var jsonResult = JsonConvert.DeserializeObject<dynamic[]>(result);
Console.WriteLine(jsonResult[0]["series1"]);
But that itself is another json string. So you'll need to then parse that again to get Category1 etc:
var jsonResult = JsonConvert.DeserializeObject<dynamic[]>(result);
var series1Result = JsonConvert.DeserializeObject<dynamic>(jsonResult[0]["series1"].ToString());
Console.WriteLine(series1Result["Category1"]); // 4.3
Live example: https://dotnetfiddle.net/hcACrM
As #Jamiec mentioned, there is inline JSON string to consider; so, you will have to deserialize twice.
For this answer, we'll avoid using dynamic altogether (without creating a custom JsonConverter) and avoid JObject (i.e. IDictionary<string, JToken>);
We'll define a Type for Categories. (For the second, iterative Deserialization)
public class Categories
{
public double Category1 { get; set; }
public double Category2 { get; set; }
public double Category3 { get; set; }
}
We'll replace dynamic[] with IEnumerable<IDictionary<string, string>>. Each dictionary has only one entry actually, so we'll simply join the Keys and Values using string.Join() in our iteration handling.
var jsonText = "[{\"series1\":\"{\\\"Category1\\\":4.3,\\\"Category2\\\":2.5,\\\"Category3\\\":3.5}\"},{\"series2\":\"{\\\"Category1\\\":2.4,\\\"Category2\\\":4.4,\\\"Category3\\\":1.8}\"},{\"series3\":\"{\\\"Category1\\\":2,\\\"Category2\\\":2,\\\"Category3\\\":3}\"}]";
var jsonResult = JsonConvert.DeserializeObject<IEnumerable<IDictionary<string, string>>>(jsonText);
var r = jsonResult.Select(i =>
{
(string Series, Categories Categories) result = (string.Join(string.Empty, i.Keys), JsonConvert.DeserializeObject<Categories>(string.Join(string.Empty, i.Values)));
return result;
}).ToArray();
(the last part uses a Deconstruct to ValueTuple)
dynamic here was actually a JObject which implements IDictionary<string, JToken>. JToken is fairly flexible (infact it works with the snippet above - including string.Join usage), but as a matter of scope and preference we deserialized to a primitive string.
Try jsonResult["series1"]["Category"]

How to map json and skip parent property?

I have this json string, which contains two elements each with a Number and a Status:
var jsonString = "{\"Errors\":[{\"Number\":9,\"Status\":\"BadRequest\"}, {\"Number\":3,\"Status\":\"BadConnection\"}]}";
As you see it has a parent property called Errors.
I have prepared this model:
public class ExceptionStructure
{
public int Number { get; set; }
public string Status { get; set; }
}
Using NewtonSoft.Json I would like to deserialize the json string into an array of ExceptionStructure objects, without also having to create a model for the parent property (as I don't really need it).
Can I do this (perhaps with some json attribute on the model class)?
I was hoping to do something like this to deserialize:
var exceptionArr = JsonConvert.DeserializeObject<ExceptionStructure>(jsonString);
JSON.NET allows you to deserialize parts of a json file. You can do this by first deserialzing the json string to a JObject, extract the relevant parts, and then deserialize those to your actual object.
JObject errors = JObject.Parse(jsonString);
IList<JToken> results = errors["Errors"].Children().ToList();
IList<ExceptionStructure> exceptions = new List<ExceptionStructure>();
foreach (JToken result in results)
{
ExceptionStructure exception= result.ToObject<ExceptionStructure>();
exceptions.Add(exception);
}
Honestly though, in your case it might be easier to just build a Errors parent class
More information can be found at http://www.newtonsoft.com/json/help/html/SerializingJSONFragments.htm
this is may be helpful you.
string s = "{\"Errors\":[{\"Number\":9,\"Status\":\"BadRequest\"}, {\"Number\":3,\"Status\":\"BadConnection\"}]}";
var jobj = JObject.Parse(s);
List<ExceptionStructure> list = jobj["Errors"].ToObject<List<ExceptionStructure>>();
OR:
string s = "{\"Errors\":[{\"Number\":9,\"Status\":\"BadRequest\"}, {\"Number\":3,\"Status\":\"BadConnection\"}]}";
List<ExceptionStructure> list = JObject.Parse(s)
.SelectToken("Errors")
.ToObject<List<ExceptionStructure>>();

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.

NewtonSoft Json retrieve data with brackets method

Currently I am using the YouTube API to get some data. The data is in json and I am trying to get a single part of it.
The problem that I am facing right now is that I want to use the following method:
WebClient client = new WebClient();
string jsonData = client.DownloadString("https://www.googleapis.com/youtube/v3/videos?id=" + videoId + "&part=contentDetails&key=");
var results = JsonConvert.DeserializeObject(jsonData);
string duration = results["items"]["contentDetails"]["duration"];
Currently this results in an error:
Cannot apply indexing with [] to an expression of type 'object'
The problem I am facing right now is that I don't know how to deserialize the object properly so I can use text indexes to get to the data. I used to do it like this before but I can't recall it and can't find anything about my method.
What you should do is define a class with all your properties that matches your JSON, and then pass that class as a type argument to the deserialization:
Results results = JsonConvert.DeserializeObject<Results>(jsonData);
I don't have more info about your JSON, but a quick definition would look like this:
public class Results
{
public Items items { get; set; }
}
public class Items
{
public ContentDetails contentDetails { get; set; }
}
public class ContentDetails
{
public string duration { get; set; }
}
If you need help converting a JSON response to C# classes, you can use converters such as http://json2csharp.com/
You can also deserialize to a dynamic type, like so:
var results = JsonConvert.DeserializeObject<dynamic>(jsonData);
Then, you just access your properties:
string duration = results.items.contentDetails.duration;
However, using dynamic is not recommended most of the time, because it effectively removes static typing, so you must be sure that duration will be a string, or it will throw a runtime exception when you try to access it.
Normally if I'm dealing with a JSON payload that I don't feel like mapping to a strong type I use JObjects which allow for LINQ to JSON.
var jObject = JObject.Parse(jsonString);
This will allow the properties of the object to be indexed using [] syntax.
More information on using LINQ to JSON in the API docs.
Get your Deserialized data into dynamic type:
dynamic results = JsonConvert.DeserializeObject(jsonData);
and then access it using .:
string duration = results.items.contentDetails.duration;
The syntax you're using is used in javascript.
Try this way:
dynamic results = JsonConvert.DeserializeObject(jsonData);
then you can do
string duration = results["items"]["contentDetails"]["duration"];
or
string duration = results.items.contentDetails.duration;
Reference to dynamic keyword on MSDN: https://msdn.microsoft.com/en-us/library/dd264736.aspx

How to parse non-array JSON?

I am trying to read json from a local .json file and parse the contents using StreamReader and Json.NET. Json & my code:
contents of .json file: {"rate":50,"information":{"height":70,"ssn":43,"name":"andrew"}}
using (var sr = new StreamReader(pathToJsonFile))
{
dynamic jsonArray = JsonConvert.DeserializeObject(sr.ReadToEnd());
foreach(var item in jsonArray)
{
Console.WriteLine(item.rate);
Console.WriteLine(item.ssn);
}
}
This gives me an error on the line foreach(var item in array): Object reference not set to an instance of an object. I am guessing this is because my json is not actually an array but that is how I am trying to parse it. How can I parse this json in order to pull out fields such as rate or ssn?
NB - please do not flag this question as a duplicate of Read and parse a Json File in C#, as that is where I got my original code from.
EDIT: As has been pointed out in other answers, jsonArray is null. That explains my error but still does not answer my question. How else can I parse this json in order to extract the desired fields?
A couple things:
If you want to manually parse out the values, you should try using JObject rather than JsonConvert.DeserializeObject. The following code should work:
dynamic jsonObject = JObject.Parse("{'rate':50,'information':{'height':70,'ssn':43,'name':'andrew'}}");
Console.WriteLine(jsonObject["rate"]);
Console.WriteLine(jsonObject["information"]["ssn"]);
However, if you know how the json is structured, you should create a .net class like:
public class Person
{
public int rate {get;set;}
public Information information {get;set;}
}
public class Information
{
public int height {get;set;}
public int ssn {get;set;}
public string name {get;set;}
}
and then use:
var person = JsonConvert.DeserializeObject<Person>(thestringtodeserialize);
That way you can have a strongly typed object.
In any case, I would check for null (DeserializeObject can obviously return null):
using (var sr = new StreamReader(pathToJsonFile))
{
dynamic jsonArray = JsonConvert.DeserializeObject(sr.ReadToEnd());
if(jsonArray != null) //new check here
{
foreach(var item in jsonArray)
{
Console.WriteLine(item.rate);
Console.WriteLine(item.ssn);
}
}
I am guessing this is because my json is not actually an array
True, the returned object is dynamic, so make use of dynamic:
var json = "{\"rate\":50,\"information\":{\"height\":70,\"ssn\":43,\"name\":\"andrew\"}}";
dynamic obj = JsonConvert.DeserializeObject(json);
Console.WriteLine("rate: {0}. ssn: {1}", obj.rate, obj.information.ssn);
See live sample here: https://dotnetfiddle.net/nQYuyX
Are you sure it's an array?
If that's the format the you expect from Json, maybe you should consider defining a class.
For example:
class SomeJsonObject
{
public int rate {get;set;}
[JsonProperty("information")] //if you want to name your property something else
public InformationObject Information {get;set;}
}
class InformationObject
{
[JsonProperty("height", NullValueHandling = NullValueHandling.Ignore)] //some other things you can do with Json
public int Height {get;set;}
public int ssn {get;set;}
public string name {get;set;}
}
This way you can just deserialize it to an object:
SomeJsonObject jsonArray = JsonConvert.DeserializeObject<SomeJsonObject>(sr.ReadToEnd());
I think your question is similar to this Deserialize JSON with C# . you can use JavaScriptSerializer
I don't get a null reference (with Json.net 6.0.3) but your code has one obvious bug:
static void Main(string[] args)
{
string s = "{'rate':50,'information':{'height':70,'ssn':43,'name':'andrew'}}".Replace('\'', '\"');
var obj = JsonConvert.DeserializeObject(s);
dynamic jsonArray = obj;
foreach (var item in jsonArray)
{
Console.WriteLine(item.rate);
Console.WriteLine(item.ssn);
}
}
The bug is Console.WriteLine(item.rate) will throw.
Your 'array' jsonArray is not actually an array, it is a dictionary!
Therefore, item=the first Key-Value-pair in the dictionary, = {"rate":50}.
You can prevent the code from throwing by getting rid of your foreach loop.
i would fire up nuget and get the JSON.net package
https://www.nuget.org/packages/Newtonsoft.Json/
http://james.newtonking.com/json
it is well documented and can save you a tonne of work.
see also http://json2csharp.com/
EDIT: you are already using this

Categories