Json.NET SelectTokens not working if json contains empty array - c#

I have a valid JSON object that contains multiple "en-US" keys, which I'm trying select. For that purpose I use the JsonPath
"$..en-US"
which is given to the SelectTokens procedure implemented by the Json.NET. It's a JSON framework for .NET . Everything is working fine and well as long as my JSON doesn't contain any empty array.
Here's an example:
var myJsonPath = "$..en-US";
var myJson =
#"{
'controls': [
{
'messages': {
'addSuggestion': {
'en-US': 'Add'
}
}
},
{
'header': {
'controls': []
},
'controls': [
{
'controls': [
{
'defaultCaption': {
'en-US': 'Sort by'
},
'sortOptions': [
{
'label': {
'en-US': 'Name'
}
}
]
}
]
}
]
}
]
}";
var jToken = JObject.Parse(myJson);
var tokens = jToken.SelectTokens(myJsonPath);
Here, the tokens variable will contain just one element! That will be the "en-US" occurence BEFORE the empty array in the 'controls' of the 'header' object. However, when I just leave this 'header' object out:
var myJson =
#"{
'controls': [
{
'messages': {
'addSuggestion': {
'en-US': 'Add'
}
}
},
{
'controls': [
{
'controls': [
{
'defaultCaption': {
'en-US': 'Sort by'
},
'sortOptions': [
{
'label': {
'en-US': 'Name'
}
}
]
}
]
}
]
}
]
}";
I will get all the 3 occurencies of the "en-US" as expected. Btw, if I validate my JsonPath on the first JSON object (i.e. which contains an empty array) in an online tool, then as expected, I get all the three "en-US" cases. This diverges from what I'm getting from the Json.NET. I'm wondering whether it's a bug or do I have to handle this case manually somehow?

This is a bug that has been fixed. Upgrade to the latest version of Json.NET.

If you're in the same situation as me where you're a bit stuck with respect to updating your version of Json.NET, you can work around the issue by doing something along the lines of this:
IEnumerable<JValue> vals = jToken
.Desecendants()
.Where(w => w is JProperty && w.Name=="en-US")
.Select(s => s.Value);
Hope that helps! The vals array will contain the same tokens you would have gotten using the selector you were trying to use before.

Related

Reformatting a JSON in C#

I have a JSON response here,
{
"Items": [
{
"Key": {
"timestamp": "2022-11-06T20",
"value": 100.80
}
},
{
"Key": {
"timestamp": "2022-11-07T08",
"value": 100.90
}
}
]
}
That I would like to reformat to this:
{
"Key": [
{
"timestamp": "2019-01-08T20",
"value": 12.44
},
{
"timestamp": "2018-12-12 16:23:00",
"value": 12.45
}
]
}
For all responses, the Key must only be on the top once followed by an array of timestamps and values, and remove the Items parent value completely. I have tried doing this and messing around with the implementation, but I keep receiving multiple different errors. Is this the correct idea to do it or is there a better way to implement this?
JObject obj = JObject.Parse(jsonOutput);
JObject newObj = new JObject();
new JProperty("KEY", new JArray(
.Children<JProperty>()
.Select(j => new JObject(
new JProperty("timestamp", j.Value["timestamp"]),
new JProperty("value", j.Value["value"])
)
)
)
);
jsonOutput = newObj.ToString();
What is the correct way to implement this idea? Thanks!
This can be done very easily by combining
SelectTokens() with a JSONPath wildcard operator for the Items[*] array to select all required JSON objects.
Serialization of an anonymous type object to create the required output structure.
Thus:
var query = obj.SelectTokens("Items[*].Key");
jsonOutput = JsonConvert.SerializeObject(new { Key = query }, Formatting.Indented);
Demo fiddle here.
you can fixed it in one line
jsonOutput = new JObject {new JProperty("Key", ((JArray)JObject.Parse(jsonOutput)
["Items"]).Select(s => s["Key"]))}.ToString();

C# JSON Deserialise / Get Data Pair from Data Name at Runtime

I want to create a method with the C# Newtonsoft library that can take in a parameter value to return the JSON data value, without needing to know and create a class beforehand, but all I can find are examples to deserialise into a class or into a dynamic object, both needing to know JSON structure prior at development time
Here's an example of what the kind of JSON format I'm expecting, but is subject to change:
{
"Input":
[
{
"Name": "foo"
},
{
"Name": "bar"
},
]
"Output":
[
{
"Name": "bob"
},
{
"Name": "builder"
},
]
}
I'm locked into using Newtonsoft library to work on the JSON file, or do it myself from scratch as it's an embedded system.
You can use JObject. If you deserialize a class without the type it will be deserialized to JObject. You would access your JObject values with named index which is obviously your property name. Other type of interest to you is JArray. This all resides in namespaces:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Example:
So the example with your JSON would be:
var json = #"{
""Input"":
[
{ ""Name"": ""foo"" },
{ ""Name"": ""bar"" }
],
""Output"":
[
{ ""Name"": ""bob"" },
{""Name"": ""builder""}
]
}";
var obj = JsonConvert.DeserializeObject(json) as JObject;
var input = obj["Input"] as JArray;
var inputArray = input[0]["Name"];
This would get the first element in array that is in Input field - "foo".

Remove property name from Json when its array

I am converting XML to JSON.
Input:
<emp
id="17377"/>
<CustomerList>
<Customer
id="67149"/>
<Customer id="64260"/>
</CustomerList>
OutPut:
"emp": {
"id": "17377"
},
"CustomerList": {
"Customer": [
{
"id": "67149"
},
{
"id": "64260"
}
]
}
But I need the below output. But I can not remove <Customer from <CustomerList> in the input. Also Please note that I need accept dynamic name
of array input. But always i want to remove the inner property name to be removed. in this example its Customer.But I may get MarkList->Mark then I need to remove remove Mark, etc.,:
"emp": {
"id": "17377"
},
"CustomerList": [
{
"id": "67149"
},
{
"id": "64260"
}
]
Is this possible please.
I use below code to convert XML to Json:
var xml = new XmlDocument();
xml.XmlResolver = null;
xml.LoadXml(richTextBox1.Text);
var jsonText = JsonConvert.SerializeXmlNode(xml,Newtonsoft.Json.Formatting.Indented);
Note:
One solution would be find the char "[" and remove before "[" and after "{".
This is not possible, as it is simply trying to change to JSON scheme in which it was orignally built.
what you can do, is use JObject to change the value of customer to feet your needs:
JObject rss = JObject.Parse(json);
JObject customers = rss.SelectToken("CustomerList");
customers ["Customer"] = newValue;
This is the snippet, modify this in your code to feet your needs.

JSON.net - Write into JSON / JObject using path string

I have a little utility where we extract values from JSON using JObject.SelectToken(path). We need to determine the paths at run-time. Works perfectly.
What I now need to do is to write back into the JSON (JObject or other) using the same path string. I've hunted and searched and I can't quite find if there is anything that does this quite as cleanly as SelectToken does for reading.
(I'm also stuck in 3.5 CF)
For example, something like:
... JObject read in already ...
var theJToken = theJObject.SelectToken("animals.cat[3].name");
theTJoken.SetValue("Bob"); // Of course this doesn't exist
... serialize it ...
JToken.SelectToken actually returns a JToken which can be modified using JToken.Replace. You can use that to replace the node within your JSON object, mutating the original object.
JObject o = JObject.Parse(#"{ 'cats': [
{ 'name': 'cat 1' },
{ 'name': 'cat 2' },
{ 'name': 'cat 3' } ] }");
// get the token
JToken secondCatName = o.SelectToken("cats[1].name");
// replace the name
secondCatName.Replace("meow");
// and the original object has changed
Console.WriteLine(o.ToString());
// { "cats": [ { "name": "cat 1" }, { "name": "meow" }, { "name": "cat 3" } ] }

Generate lightweight JSON using DataContractJsonSerializer

I'm trying to generate JSON using C# and DataContractJsonSerializer in .Net 3.5. The problem is that I can't figure out how to build the structure correct for the result I need.
I've tried to reproduce PHP's associative arrays using both hashtables, list objects and arraylists but can't figure out how to generate my result in the best way using DataContractJsonSerializer without having to create my own recursive loop for building JSON.
The closest approach is using the a Dictionary < string, Dictionary < string, string>> aproach but the result is too big as I can't "name" the keys.
This is what I get:
[
{
"Key":"status",
"Value":[
{
"Key":"value",
"Value":"ok"
}
]
},
{
"Key":"1",
"Value":[
{
"Key":"name",
"Value":"Result1"
},
{
"Key":"details",
"Value":"Result 1 details"
}
]
},
{
"Key":"2",
"Value":[
{
"Key":"name",
"Value":"Result2"
},
{
"Key":"details",
"Value":"Result 2 details"
}
]
},
{
"Key":"caller",
"Value":[
{
"Key":"value",
"Value":"1135345"
}
]
}
]
This is what I want:
{
"status":"ok",
"response":[
{
"id":1,
"name":"Result1"
"details":"Result 1 details"
},
{
"id":2,
"name":"Result2"
"details":"Result 2 details"
},
{
"id":3,
"name":"Result3"
"details":"Result 3 details"
],
"caller":"1135345"
}
Does anybody have a clue how I can generate this piece of JSON using C# without having to load the entire Json.NET framework? I need this to be generated as fast as possible as this project aims to become an site search engine.
You should look at using the JavaScriptSerializer. It's part of the .NET framework (distributed in System.Web.Extensions). To get the result you want you can do this:
var results = new[]
{
new{id=1,name="Result 1"},
new{id=2,name="Result 2"},
new{id=3,name="Result 3"}
};
var js = new JavaScriptSerializer();
var result = js.Serialize(new
{
status = "ok",
response = results,
caller = 1135345
});
You can either use anonymous classes, or any existing ones. Works perfectly fine :)
The return value of this call would be:
{"status":"ok","response":[{"id":1,"name":"Result 1"},{"id":2,"name":"Result 2"},{"id":3,"name":"Result 3"}],"caller":1135345}
Using the JavaScriptSerializer rather than DataContractJsonSerializer on a Dictionary will remove the Key/Value json attributes and make them into a "keyed" array.
http://msdn.microsoft.com/en-us/library/bb412168.aspx
Have you attributed your classes with the [DataContract] and [DataMember] decorators?
http://msdn.microsoft.com/en-us/library/bb412179.aspx

Categories

Resources