I have a DataTable which I convert to json using JsonConvert.SerializeObject. It works fine and here is the results:
[
{
"String": null,
"Int": 1,
"Double": 1.0,
"Boolean": false,
"Date": "2022-09-30T00:00:00",
"DateTime": "2022-09-30T00:00:00",
"List of strings": [
"A",
"C"
],
"List of floats": [
1.0,
2.0
],
"List of dates": "[datetime.date(2022, 9, 30)]",
"Dictionary String - Flaot": {
"A": 1.0
},
"Dictionary String - List of floats": {
"A": [
1.0
]
}
},
{
"String": null,
"Int": 2,
"Double": 2.0,
"Boolean": true,
"Date": "2021-01-01T00:00:00",
"DateTime": "2021-01-01T00:00:00",
"List of strings": [
"B",
"D"
],
"List of floats": [
3.0,
4.0
],
"List of dates": "[datetime.date(2022, 1, 1), datetime.date(2022, 2, 2)]",
"Dictionary String - Flaot": {
"B": 2.0,
"C": 3.0
},
"Dictionary String - List of floats": {
"B": [
2.0,
3.0
],
"C": [
12.0
]
}
}
]
However when I used JsonConvert.DeserializeObject<DataTable>(aboveJson) then I got an error:
JsonSerializationException: Unexpected JSON token when reading DataTable: StartObject. Path '[0]['Dictionary String - Flaot']', line 18, position 34.
at Newtonsoft.Json.Converters.DataTableConverter.GetColumnDataType(JsonReader reader)
at Newtonsoft.Json.Converters.DataTableConverter.CreateRow(JsonReader reader, DataTable dt, JsonSerializer serializer)
at Newtonsoft.Json.Converters.DataTableConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
How can I fix that?
Text added to be able to add question...
Text added to be able to add question...
Text added to be able to add question...
Text added to be able to add question...
you have to flatten all properties with objects
var jArray = JArray.Parse(json);
foreach (JObject item in jArray)
{
foreach (var prop in item.Properties())
{
if (prop.Value.Type == JTokenType.Array) prop.Value = string.Join(",", prop.Value);
else if (prop.Value.Type == JTokenType.Object)
{
var newValue = "";
foreach (var prop2 in ((JObject)prop.Value).Properties())
{
if (prop2.Value.Type == JTokenType.Array) newValue = newValue + prop2.Name + ": " + string.Join(",", prop2.Value) + "; ";
else newValue = newValue + prop2.Name + ": " + (string)prop2.Value + "; ";
}
prop.Value = newValue;
}
}
}
DataTable dataTable=jArray.ToObject<DataTable>();
output
[
{
"String": null,
"Int": 1,
"Double": 1.0,
"Boolean": false,
"Date": "2022-09-30T00:00:00",
"DateTime": "2022-09-30T00:00:00",
"List of strings": "A,C",
"List of floats": "1,2",
"List of dates": "[datetime.date(2022, 9, 30)]",
"Dictionary String - Flaot": "A: 1; ",
"Dictionary String - List of floats": "A: 1; "
},
{
"String": null,
"Int": 2,
"Double": 2.0,
"Boolean": true,
"Date": "2021-01-01T00:00:00",
"DateTime": "2021-01-01T00:00:00",
"List of strings": "B,D",
"List of floats": "3,4",
"List of dates": "[datetime.date(2022, 1, 1), datetime.date(2022, 2, 2)]",
"Dictionary String - Flaot": "B: 2; C: 3; ",
"Dictionary String - List of floats": "B: 2,3; C: 12; "
}
]
Related
I have a json file like this:
[
{
"key1": {
"find": 5,
"count": 65,
"name": "Parser"
},
"init": {
"key2": {
"find": 5,
"count": 15,
"name": "Some"
},
"arr": [
{
"key2": {
"find": 8,
"count": 32,
"name": "Object"
},
"temp": {
"pay": null
}
}
]
}
},
{
"key3": {
"find": 5,
"count": 23,
"name": "String"
},
"classes": [],
}
]
And I want to get list of all nodes that contains key "find" and value "5". The result have to be:
{
"find": 5,
"count": 65,
"name": "Parser"
},
{
"find": 5,
"count": 15,
"name": "Some"
},
{
"find": 5,
"count": 23,
"name": "String"
}
The difficulty is that the nesting can be any, but I need to get only those nodes that contain key "find" and the value "5" for it. How can I go through the entire file and get the nodes I need?
You can use JToken for this purpose, use the below function to find the nodes.
public void FindNodes(JToken json, string name, string value, List<JToken> nodes)
{
if (json.Type == JTokenType.Object)
{
foreach (JProperty child in json.Children<JProperty>())
{
if (child.Name == name && child.Value.ToString() == value)
{
nodes.Add(child);
}
FindNodes(child.Value, name, value, nodes);
}
}
else if (json.Type == JTokenType.Array)
{
foreach (JToken child in json.Children())
{
FindNodes(child, name, value, nodes);
}
}
}
Use the method in this way,
var json = JsonConvert.DeserializeObject<JToken>(jsonString);
var nodes = new List<JToken>();
FindNodes(json, "find", "5", nodes);
Is there a good way to iterate through a deserialized JSON object from last to first? i.e., the following code I can iterate and record 'n' values from first to last until it meets my count condition for further processing:
protected void testButton_Click(object sender, EventArgs e)
{
List<decimal> priceHistory = new List<decimal>();
var client = new RestClient("https://api.tdameritrade.com/v1/marketdata/" + inputSymbol + "/pricehistory?periodType=year&period=1&frequencyType=daily&frequency=1&needExtendedHoursData=false");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer " + ReadAccessToken());
request.AddParameter("text/plain", "", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
dynamic history = JsonConvert.DeserializeObject<JObject>(response.Content);
int count = 0;
foreach (var child in history["candles"])
{
count += 1;
while (count <= 100)
{
decimal close = child["close"];
priceHistory.Add(close);
break;
}
}
decimal simpleMovingAverage = priceHistory.Average();
responseLabel.Text = simpleMovingAverage.ToString();
//output is taking the first 100 close prices returned from the JSON response and averaging those values.
}
However, what I cannot find a solution to is how to begin at the last nested JSON object and add values from a back-to-front style. I am trying to reverse the aggregation from counting first nested object + 'n' next values sequential to the previous down the list, to actually taking the last nested JSON object in the response and average it with each of its previous 'n' values until the count condition has been met.
EDIT
JSON example with arbitrary 'n' period for reference:
{
"candles": [
{
"open": 172.51,
"high": 176.36,
"low": 171.5,
"close": 171.86,
"volume": 2292877,
"datetime": 1582524000000
},
{
"open": 172.18,
"high": 172.945,
"low": 165.8,
"close": 166.65,
"volume": 2358560,
"datetime": 1582610400000
},
{
"open": 168.28,
"high": 169.38,
"low": 165.61,
"close": 166.92,
"volume": 2545782,
"datetime": 1582696800000
},
{
"open": 164.4,
"high": 166.55,
"low": 159.96,
"close": 159.99,
"volume": 2957507,
"datetime": 1582783200000
},
{
"open": 155.29,
"high": 158.92,
"low": 152.66,
"close": 156.48,
"volume": 3053876,
"datetime": 1582869600000
},
{
"open": 157.92,
"high": 164.0,
"low": 156.55,
"close": 163.92,
"volume": 2532128,
"datetime": 1583128800000
}
],
"symbol": "DE",
"empty": false
}
You can use the Reverse() method of the JArray class. Just cast your history["candles"] JTokeninto a JArray, and call reverse.
void ParseJson(string json)
{
var history = JObject.Parse(json);
var candles = (history["candles"] as JArray).Reverse();
foreach (var child in candles)
{
Console.WriteLine(child["close"].ToString());
}
Console.Read();
}
I have two JSON objects -
json1 = {
"payload": {
"firstName": "John",
"lastName": "Doe",
"code": "test1",
"arrayProp1": [1, 2, 3],
"arrayProp2": [{
"prop1": "value1",
"prop2": "value2"
},
{
"prop1": "2_value1",
"prop2": "2_value2"
}
]
}
}
json2 = {
"payload": {
"code": "newCode",
"arrayProp1": [3,4],
"arrayProp2": [{
"prop1": "newValue1",
"prop2": "newValue2"
}
]
}
}
If I use the built-in merge (json1.Merge(json2)) the result obtained is -
result : {
"payload": {
"firstName": "John",
"lastName": "Doe",
"code": "newCode",
"arrayProp1": [1, 2, 3, 3, 4],
"arrayProp2": [{
"prop1": "value1",
"prop2": "value2"
},
{
"prop1": "newValue1",
"prop2": "newValue2"
},
{
"prop1": "2_value1",
"prop2": "2_value2"
}
]
}
}
Expected result -
{
"payload": {
"firstName": "John",
"lastName": "Doe",
"code": "newCode",
"arrayProp1": [3, 4],
"arrayProp2": [{
"prop1": "newValue1",
"prop2": "newValue2"
}]
}
}
I want to replace the parent property values of json1 based on values provided in json2.
I tried to write a function and this is the current version I have -
string Merge(string req1, string req2) {
try
{
JObject json1 = JObject.Parse(req1);
JObject json2 = JObject.Parse(req2);
foreach (var a in json2.DescendantsAndSelf())
{
if (a is JObject obj)
{
foreach (var prop in obj.Properties())
{
if(json1.SelectTokens(prop.Path).Any())
{
json1[prop.Path] = prop.Value;
}
}
}
}
req1 = json1.ToString();
}
catch(Exception ex)
{
//do nothing
}
return req1; }
There are 2 problems here -
"payload" is identified as property and json1 is replaced fully by json2 because of which I lose some of its properties.
After being replaced, when the loop continues to run, say property 'code' is to be updated, then the property path is payload.code, so on the line json1[prop.path] = prop.Value, instead of updating the existing code in the payload, it creates a new property called payload.code with value "newcode"
The final result of the code above is -
{
"payload": {
"code": "newCode",
"arrayProp1": [3, 4],
"arrayProp2": [{
"prop1": "newValue1",
"prop2": "newValue2"
}],
"payload.code": "newCode",
"payload.arrayProp1": [3, 4],
"payload.arrayProp2": [{
"prop1": "newValue1",
"prop2": "newValue2"
}],
"payload.arrayProp1[0].prop1": "newValue1",
"payload.arrayProp1[0].prop2": "newValue2"
}
}
Can someone please help me with this?
Your requirement is that array contents are replaced rather than concatenated when merging two JSON objects with JContainer.Merge(). You can achieve this via the JsonMergeSettings.MergeArrayHandling setting, which has the following values:
Concat 0 Concatenate arrays.
Union 1 Union arrays, skipping items that already exist.
Replace 2 Replace all array items.
Merge 3 Merge array items together, matched by index.
Specifically MergeArrayHandling.Replace will work as required:
json1.Merge(json2, new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Replace
});
Demo fiddle here.
I'm loading some information to my Xamarin application through multiple json strings. When I run the application, it gives me this error:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[APIPost]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List
that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'postid', line 1, position 9.'
Code to get the json:
HttpClient client = new HttpClient();
var result = await client.GetStringAsync($"{APIConfig.Uri}/post/getPost/{APIConfig.Token}/{User.ID}/{User.Token}");
List<APIPost> response = JsonConvert.DeserializeObject<List<APIPost>>(result);
foreach (APIPost post in response)
{ //Code }
Class APIPost:
class APIPost
{
public string state { get; set; }
public string postid { get; set; }
public string userid { get; set; }
public string image { get; set; }
public string captation { get; set; }
public string location { get; set; }
public string date { get; set; }
public string likes { get; set; }
}
This is the json I get:
{
"postid": "2",
"userid": "2",
"image": "asdasdasd",
"captation": "asdasd",
"location": null,
"date": "2019-07-29 20:24:28",
"likes": "4"
}{
"postid": "1",
"userid": "2",
"image": "susfdfjsadv",
"captation": "This is just a test.",
"location": null,
"date": "2019-07-29 19:58:04",
"likes": "2"
}
The problem isn't with C# or with the JSON serialization library you're using. The JSON you're receiving from the server is invalid. As such, no standard JSON parsing library can be expected to successfully parse it. You'll either need to write your own, or correct the JSON to be valid.
A valid object would look like:
{
"postid": "2",
"userid": "2",
"image": "asdasdasd",
"captation": "asdasd",
"location": null,
"date": "2019-07-29 20:24:28",
"likes": "4"
}
And a valid array of objects would look like:
[
{
"postid": "2",
"userid": "2",
"image": "asdasdasd",
"captation": "asdasd",
"location": null,
"date": "2019-07-29 20:24:28",
"likes": "4"
},
{
"postid": "1",
"userid": "2",
"image": "susfdfjsadv",
"captation": "This is just a test.",
"location": null,
"date": "2019-07-29 19:58:04",
"likes": "2"
}
]
There's no such thing as "multiple JSON" within the same structure. You either have a valid structure or you don't. You can certainly have multiple structures, but you can't have them all mashed together into one like that.
In short... fix the server-side code to send a valid response.
Have you any control over the source of that json?
You need to provide something like:
[
{...},
{...}
]
And you will be fine
As #lasse-v-karlsen says in a comment, the server should be fixed but there's a way to parse this with Newtonsoft.Json (in case we're in one of those JSON-streaming scenarios):
using (var file = new StreamReader(#"D:\Temp\test.json", Encoding.UTF8))
using (var reader = new Newtonsoft.Json.JsonTextReader(file))
{
reader.SupportMultipleContent = true;
var serializer = JsonSerializer.CreateDefault();
serializer.Deserialize(reader).Dump();
while (reader.Read())
serializer.Deserialize(reader).Dump();
}
In case you're not fond on adding the dependency to json.net, then I've cooked a very simple JSON splitter:
IEnumerable<string> SplitJsonStream(string jsonString)
{
IEnumerable<string> splitJsonStreamInner(char? head, string tail, uint braceNestingLevel, bool insideSubString, string acc)
{
if (!head.HasValue)
{
if (braceNestingLevel != 0)
throw new ArgumentException("jsonString seems to be invaid");
yield break;
}
char? newHead = null;
string newAcc = null;
if (!String.IsNullOrEmpty(tail))
{
newHead = tail.First();
}
if (head.HasValue)
{
newAcc = acc + head.Value;
}
var newTail = newHead == null ? null : tail.Substring(1);
if (!insideSubString && head == '"') {
foreach(var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel, true, newAcc))
yield return subAcc;
yield break;
}
if (insideSubString)
{
if (head == '"')
{
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel, false, newAcc))
yield return subAcc;
yield break;
}
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel, insideSubString, newAcc))
yield return subAcc;
yield break;
}
if (head == '{')
{
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel + 1, insideSubString, newAcc))
yield return subAcc;
yield break;
}
else if (head == '}')
{
if (braceNestingLevel == 0)
throw new ArgumentException("jsonString seems to be invalid");
var newNestingLevel = braceNestingLevel - 1;
if (newNestingLevel == 0) {
yield return newAcc;
newAcc = String.Empty;
}
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, newNestingLevel, insideSubString, newAcc))
yield return subAcc;
yield break;
}
else
{
foreach (var subAcc in splitJsonStreamInner(newHead, newTail, braceNestingLevel, insideSubString, newAcc))
yield return subAcc;
yield break;
}
}
jsonString = jsonString.Trim();
if (jsonString == string.Empty)
yield break;
if (!jsonString.StartsWith("{"))
throw new ArgumentException("jsonString should start with {");
if (!jsonString.EndsWith("}"))
throw new ArgumentException("jsonString should end with }");
foreach (var sub in splitJsonStreamInner(jsonString.First(), jsonString.Substring(1), 0, false, String.Empty))
yield return sub;
}
The proof that the above works is this unit test:
[Fact()]
public void JsonSplitterTests()
{
Assert.Empty(SplitJsonStream(String.Empty));
Assert.Throws<ArgumentException>(() => SplitJsonStream("x").Count()); // not startswith {
Assert.Throws<ArgumentException>(() => SplitJsonStream("{x").Count()); // not endswith }
var fooJson = "{ \"foo\": 1 }";
var splitted = SplitJsonStream(fooJson);
Assert.Equal(1, splitted.Count());
Assert.Equal(fooJson, splitted.ElementAt(0));
var barJson = "{ \"bar\": 2 }";
splitted = SplitJsonStream(fooJson + barJson);
Assert.Equal(2, splitted.Count());
Assert.Equal(fooJson, splitted.ElementAt(0));
Assert.Equal(barJson, splitted.ElementAt(1));
var bazJson = "{ \"baz\": 3 }";
splitted = SplitJsonStream(fooJson + barJson + bazJson);
Assert.Equal(3, splitted.Count());
Assert.Equal(fooJson, splitted.ElementAt(0));
Assert.Equal(barJson, splitted.ElementAt(1));
Assert.Equal(bazJson, splitted.ElementAt(2));
// ends with bad nesting level
Assert.Throws<ArgumentException>(() => SplitJsonStream("{{x}").Count());
Assert.Throws<ArgumentException>(() => SplitJsonStream("{x}}").Count());
// edge case
Assert.Equal(1, SplitJsonStream("{ \"foo\": \"{\" }").Count());
//whitespace in front
Assert.Equal(2, SplitJsonStream("\n{ \"foo\": 1 }{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("\r\n{ \"foo\": 1 }{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("\t{ \"foo\": 1 }{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream(" { \"foo\": 1 }{ \"bar\": 2 }").Count());
//whitespace at the end
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }{ \"bar\": 2 }\n").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }{ \"bar\": 2 }\r\n").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }{ \"bar\": 2 }\t").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }{ \"bar\": 2 } ").Count());
//whitespace in the middle
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }\n{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }\r\n{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 }\t{ \"bar\": 2 }").Count());
Assert.Equal(2, SplitJsonStream("{ \"foo\": 1 } { \"bar\": 2 }").Count());
}
Would it be possible to sort the JArray below by col2 for example?
[
{
"col1": "thiscol",
"col2": "thisval"
},
{
"col1": "thiscol2",
"col2": "thisval2"
},
{
"col1": "thiscol3",
"col2": "thisval3"
}
]
If converting this to an Array is the only solution then how could I do this?
I don't think you can sort a JArray in place, but you can sort the contents and load the result into another JArray. Would this work for you?
string json = #"
[
{
""col1"": ""foo"",
""col2"": ""bar""
},
{
""col1"": ""baz"",
""col2"": ""quux""
},
{
""col1"": ""fizz"",
""col2"": ""bang""
}
]";
JArray array = JArray.Parse(json);
JArray sorted = new JArray(array.OrderBy(obj => (string)obj["col2"]));
Console.WriteLine(sorted.ToString(Formatting.Indented));
Output:
[
{
"col1": "fizz",
"col2": "bang"
},
{
"col1": "foo",
"col2": "bar"
},
{
"col1": "baz",
"col2": "quux"
}
]
Fiddle: https://dotnetfiddle.net/2lTZP7