I am trying to use C# LINQ to build a JObject. I know that I can use a loop, such as,
var jobj = new JObject();
foreach (var field in fieldList)
{
jobj[field.Name] = new JValue(field.Value);
}
Is it possible to replace the loop with LINQ? I tried this,
var data = fieldList.Select(field => new KeyValuePair<string, JValue>(field.Name, new JValue(field.Value)));
var jobj = new JObject(data);
but it fails with this error:
Could not determine JSON object type for type System.Collections.Generic.KeyValuePair`2[
System.String,Newtonsoft.Json.Linq.JValue].
Here
jobj[field.Name] = new JValue(field.Value);
you actually are calling the following JObject indexer:
public JToken this[string propertyName] { get; set; }
i.e. you are setting the JObject property.
So the LINQ equivalent will be like this:
var data = fieldList.Select(field => new JProperty(field.Name, field.Value));
var jobj = new JObject(data);
Well, I don't know if it's any prettier, but you could use Aggregate:
fieldList.Aggregate(new JObject(), (obj, next) => {obj[next.Name] = new JValue(next.Value); return obj;})
It would be nice if JObject had a chainable API, but it doesn't seem to.
Related
I'm trying to deserialize a pretty ugly JSON provided by an external REST API and am wondering about the "proper" way to do that (I'm using System.Text.Json in .net 6). Details follow:
I have a model for the data:
class DeviceData{
//lots of properties
}
which works fine (i.e I can just JsonSerializer.Deserialize<DeviceData> the response) when making an API query for a single instance, since it returns a nice JSON one would expect:
{
"property1_name": value,
"property2_name": value,
...
}
The problem begins when I use the batch query provided by the API, since the response to api_url/batch?=device1,device2,... looks as if someone failed to make an array (the device1s are alphanumeric strings pulled form a database) is:
{
"names":[
"device1",
"device2",
...
],
"device1":{
"stuff_i_dont_need": value,
"device1": {
"property1_name": value,
"property2_name": value,
...
}
}
"device2":{
...
}
...
}
The double nesting of dynamic property names means I can't just deserialize the second response as a dictionary of <string, myclass> pairs. I managed to hack something together using JsonDocument but it's extremly ugly and it feels like there should be a nice short way to do that with just JsonSerializer and maybe some reader overrides.
Using Deserialize subsections of a JSON payload from How to use a JSON document, Utf8JsonReader, and Utf8JsonWriter in System.Text.Json as template you could do something like this:
JsonNode root = JsonNode.Parse(json)!;
Dictionary<string, X> devices = new();
foreach(string name in root["names"]!.AsArray()) {
var o = root[name][name].AsObject();
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
o.WriteTo(writer);
writer.Flush();
X? x = JsonSerializer.Deserialize<X>(stream.ToArray());
var innerJson = root[name][name].ToJsonString();
devices[name] = x;
}
foreach(var d in devices) Console.WriteLine($"{d.Key}: {d.Value}");
This prints
device1: X { property1_name = 12, property2_name = 13 }
device2: X { property1_name = 22, property2_name = 23 }
I'm not sure if this is faster/better than calling ToJsonString():
JsonNode root = JsonNode.Parse(json)!;
Dictionary<string, X> devices = new();
foreach(string name in root["names"]!.AsArray()) {
var innerJson = root[name][name].ToJsonString();
devices[name] = JsonSerializer.Deserialize<X>(innerJson);
}
foreach(var d in devices) Console.WriteLine($"{d.Key}: {d.Value}")
If you're after fancy you could go full LINQ:
JsonNode root = JsonNode.Parse(json)!;
Dictionary<string, X> devices = root["names"]!.AsArray()
.Select(name => (string)name)
.ToDictionary(
keySelector: name => name,
elementSelector: name => System.Text.Json.JsonSerializer.Deserialize<X>(root[name][name].ToJsonString()));
foreach(var d in devices) Console.WriteLine($"{d.Key}: {d.Value}");
Both print
My JSON is a very long one and i am fetching only one section "parent_crumbs" from the long JSON
...................,
"parent_crumbs":["Platforms","New platform"],
"promise_by":"2016-08-01",
....
The code I used to fetch value of "parent_crumbs" is
JObject lp_p = JObject.Parse(response_json);
string val= lp_p["parent_crumbs"].ToString();
This returns the following value
"[\r\n \"Platforms\",\"New platform\"\r\n]"
Now I have to do a comparison with the first value from the array as the string is available in a Dictionary as key value and if available return ID
Packages = new Dictionary<string, int>();
Packages.Add("Platforms", 10212);
Packages.Add("New platform", 10202);
Packages.Add("Unknown platform", 10203);
int category=
if(Packages.ContainsKey(val))
{
Packages.TryGetValue(val, out category);
}
So with current code I can't do the comparison straight away due to presence of [\r\n etc.
How to get the value as a string Array without special chars like [\r\n .
Making Model Classes for the JSON for deserialization is not preferred way for me. Since creating class is a big job for me as lot of properties are there in JSON and is dynamic of nature
We can use the below code
var input = "[\r\n \"Platforms\",\"New platform\"\r\n]";
var array =(JArray) JsonConvert.DeserializeObject(input);
bool isEqual = array[0].Value<string>() == "Platforms";
you could also convert it to array with Linq
using System.Linq;
var tmp = lp_p["parent_crumbs"].Select(x => x.ToString());
foreach (var x in tmp)
{
Console.WriteLine(x.ToString());
}
By using Select, it will help you convert it to array rather than to string
You can use DeserializeAnonymousType method for that:
var myType = new
{
parent_crumbs = new []{ "" },
promise_by = default(DateTime)
};
var result = JsonConvert.DeserializeAnonymousType(json, myType);
int category = 0;
string key = result.parent_crumbs[0];
if(Packages.ContainsKey(key))
{
Packages.TryGetValue(key, out category);
}
References: DotNetFiddle Example, DeserializeAnonymousType
I need to cast JArray which I received from browser to list of MyType object. The problem is that I don't want to use .ToObject<> extension because object sended from browser can have missing or extra values and I cannot change deserializer. I wrote my own parser to receive data from single JObject. Usage:
foreach (var _object in _jarray)
{
using (var tp = new TokenParser(_object))
{
MyType test = new MyType()
{
id = tp.ConvertToInt("token_key")
};
}
}
What I want to do is something like this
_jarray.ForEach(_object => {
using (var tp = new TokenParser(_object))
{
MyType test = new MyType()
{
id = tp.ConvertToInt("token_key")
};
}
});
I don't want to cast elements like:
_jarray.Select(x =>
(int)["token_key"]
);
I tried to wrote this extension based on LINQ equivalent of foreach for IEnumerable<T> but it seems that Action requires T as parameter type. I don't need this because JArray is an array of JObject.
Is this possible to achieve?
dynamic data = JsonConvert.DeserializeObject(result);
foreach (var field in data)
{
var deserializedData = new DeserializedData
{
Label = field.Key.ToObject<string>(),
Content = field.Value.ToObject<string>()
};
}
Note: data = {"name":"test"}
Error:'Newtonsoft.Json.Linq.JProperty' does not contain a definition for 'Key'
I think I understand what the issue is but I am not sure if there is a way to achieve this behavior or is there a different approach to handle the dynamic key value scenarios like this.
The Key will change so I don't know if its going to be name or phone or email or some other field...
In order to iterate through the children of the returned data as an IEnumerable<KeyValuePair<string, JToken>>, you need to explicitly declare the deserialized object as a JObject:
var data = JObject.Parse(result);
foreach (var field in data)
{
var deserializedData = new DeserializedData
{
Label = field.Key,
Content = field.Value.ToObject<string>()
};
}
When you do dynamic data = JsonConvert.DeserializeObject(result);, the foreach ends up calling the IEnumerable<JToken> iterator in the base class JToken. I.e. the following also works:
dynamic data = JsonConvert.DeserializeObject(result);
foreach (var field in data)
{
// "field" happens to be of type JProperty
var deserializedData = new DeserializedData
{
Label = field.Name,
Content = field.Value.ToObject<string>()
};
}
Though I prefer the strongly typed version using JObject since more checking can be done at compile time.
According to the documentation the JProperty's value contains the key, and the JProperty's children are the values. source
I know you can serialize JSON to Dictionary and object[], but the problem is that it requires you to constantly cast things all over the place:
using System.Web.Script.Serialization;
var json = new JavaScriptSerializer { }.DeserializeObject(#"{array:['item1','item2']}") as Dictionary<string, object>;
var strings = new List<string> { };
if (json != null)
{
var array = json["array"] as object[];
if (array != null)
{
foreach (var item in array.Select(i=>i as string))
{
if (!string.IsNullOrWhiteSpace(item))
{
strings.Add(item);
}
}
}
}
return strings.ToArray();
I would much rather do something more like:
try
{
var json = new JavaScriptSerializer { }.DeserializeDynamic(#"{array:['item1','item2']}") as Dictionary<string, dynamic>;
var strings = new List<string>{};
foreach (var item in json["array"])
strings.Add(item as string);
return strings.ToArray();
}
catch { return null; }
I have run into problems with object[] and number types, I'm never sure if it's going to be an int, a float, a decimal, or a double etc. I can't sanitize my json data so it reliably is only a double...
Are there any JSON Serializers for .Net 4.0 that output Dictionary and dynamic[]? That just seams like a more natural choice for working with json then Dictionary and object[]...
JSON.NET should handle your needs (with the Json.NET Dynamic Extensions).
Dictionary<string, object> and Dictionary<string, dynamic> are the same types as far runtime is concerned.
For example this works:
var dynDic =new Dictionary<string,object>{ {"test", 1}}
as Dictionary<string,dynamic>;
int value = dynDic["test"];
So if your first works:
var json = new JavaScriptSerializer { }
.DeserializeObject(#"{array:['item1','item2']}")
as Dictionary<string, object>;
then so should:
var json = new JavaScriptSerializer { }
.DeserializeDynamic(#"{array:['item1','item2']}")
as Dictionary<string, dynamic>;
But then :
foreach (var item in json["array"])
strings.Add(item as string);
doesn't use dynamic invocation so it compiles that same whether the item var was object or dynamic so if you are having problems my guess is it's in the use of dynamic.
Same rule applies to object[] and dynamic[], they are the same type, you can freely cast between them, when you cast you are just telling the compiler how to use the objects from that array.
dynamic[] dynArr = new object[]{1};
So the answer is that anything that will deserialize to Dictionary<string,object> or object[] can be used as Dictionary<string,dynamic> or dynamic[]