Flexibly edit json values in C# - c#

I've been trying to insert values from one json file into another. These structures have mostly been decided on, so I want to try and make them work as they are.
The problem i've been running into is that the provided paths may not always have the same depth and thus the coded used to insert values into that provided path needs to be flexible enough to deal with that.
These are the json files:
this json holds values, the paths to where those values should be placed in the other json file and when applicable, the index.
[
{
"path": "x_axis.data",
"value": "['','2005','2010','2015','2021','2026']",
"index": null
},
{
"path": "series.data",
"value": "[77.0,70.0,74.0,0.0,0.0,0.0]",
"index": 0
},
{
"path": "series.data",
"value": "[0.0,0.0,0.0,99.0,100.0,100.0]",
"index": 1
}
]
This json is a template structure that will be reused. it' based on the Echarts documentation, since we use that library to show charts.
{
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "shadow"
}
},
"legend": {
"data": [ "One", "Two" ]
},
"grid": {
"left": "3%",
"right": "4%",
"bottom": "3%",
"containLabel": true
},
"xAxis": [
{
"type": "category",
"data": []
}
],
"yAxis": [
{
"type": "value"
}
],
"series": [
{
"name": "One%",
"type": "bar",
"stack": "Stack 1",
"emphasis": {
"focus": "series"
},
"itemStyle": {
"color": "blue"
},
"data": []
},
{
"name": "Two%",
"type": "bar",
"stack": "Stack 1",
"emphasis": {
"focus": "series"
},
"itemStyle": {
"color": "red"
},
"data": []
}
]
}
Currently my method converts the path into a string array with text formatting that matches the names in the template json. I am however unsure how to use it or whether it was the right choice.
Here is my code:
public static string GenerateChart(string templateString, string replacementValuesString)
{
string result = "";
dynamic templateJson = JObject.Parse(templateString);
dynamic replacementJson = JArray.Parse(replacementValuesString);
foreach (var item in replacementJson)
{
string[] pathArr = CreatePathArray(item);
//check for index
if (item.index != null)
{
//TODO: Implement code that handles the index
templateJson.path = item.value;
}
else
{
templateJson.path = item.value;
}
}
result = templateJson.ToString();
return result;
}
I used Newtonsoft to parse the json. My team decided against using models to deserialise the Json because they wanted to be able to only edit the json files for maintenance of the program.
My question to anyone willing to help is how would you go about solving this problem?
If I've failed to explain bits properly please let me know so I can clarify.
Thank you for your help!
Edit 1: I forgot to mention how the path is split into the array. It's split at the period, so "x_axis.data" becomes "["xAxis", "data"]".
Thanks to E. Shcherbo for helping me find a solution
I ended up modifying the code he suggested to fit my project as a few minor things were causing issues. Here's the code I ended up using and will now continue to polish:
var replacementJson = JArray.Parse(replacementValuesString);
JToken templateJson = JObject.Parse(templateString);
foreach (var item in replacementJson)
{
string[] pathArr = CreatePathArray(item);
//The method using ^1 provided by E. Shcherbo didn't work for me here.
//I'm guessing it's because I'm working in an old version of dotnetframework.
var valuePropName = pathArr[pathArr.Length - 1];
//Using this loop to "dive deeply" into the template json as E. put it, we find the correct path and then modify it.
for (int i = 0; i < pathArr.Length - 1; i++)
{
var pathKey = pathArr[i];
templateJson = templateJson[pathKey];
if (templateJson == null)
{
Console.WriteLine("Template path given is invalid.");
break;
}
}
// if my item contains a value for index, it will be used. Otherwise it's safe to assume we're not dealing with an array.
if (item["index"] != null)
{
UpdateValueInTemplate(templateJson, item["index"], valuePropName, item["value"]);
}
else
{
UpdateValueInTemplate(templateJson, valuePropName, item["value"]);
}
templateJson = templateJson.Root;
}

First of all you probably don't need dynamic type here, because you know types of your objects at compile time JObject, JArray, JToken, so this is much safer to use them (you can go with var).
I'll describe two approaches I see here (both of them reuse the UpdateValueInTemplate method).
Note: be careful with different edge cases which might be possible depending on what you can and can't assume about your data.
Giving your approach with an array which represents the path, you can just iterate through the array and go deeply and deeply in your templateJson. Something like this
JToken templateJson = JObject.Parse(templateString);
var valuePropName = pathArr[^1]; // ^1 means first from the end
for (int i = 0; i < pathArr.Length - 1; i++)
{
var pathKey = pathArr[i];
templateJson = templateJson[pathKey];
if (templateJson is null)
// The path is wrong. Do whatever validation is expected
}
UpdateValueInTemplate(templateJson, valuePropName, item["value"]);
You can also pass your path from replacementJson (without the last property to get an object where the last property is rather than value of the property) into the SelectToken method. Something like this:
var path = item["path"].ToString();
var lastPathSplitter = item.Path.LastIndexOf('.');
var pathWithoutLastProperty = path.Substring(0, lastPathSplitter);
var valPropName = item.Path.Substring(lastPathSplitter + 1);
var jsonToUpdate = templateJson.SelectToken(pathWithoutLastProperty);
if (jsonToUpdate is null)
// The path is wrong. Do whatever validation is expected
UpdateValueInTemplate(jsonToUpdate, valPropName, item["value"]);
Then the UpdateValueInTemplate method can look like this:
void UpdateValueInTemplate(JToken tokenToUpdate, string valPropName, JToken value) {
tokenToUpdate[valPropName] = value;
}
You can also reduce manipulations with your path if you pass the entire path to the SelectToken and then will use yourToken.Parent.Parent[valPropName] = value to update the json.
UPDATE
I didn't notice that you also want to handle cases where the first property of the path can point to the array. So when this is the case it can be handled like this:
JToken templateJson = JObject.Parse(templateString);
var valuePropName = pathArr[^1]; // ^1 means first from the end
for (int i = 0; i < pathArr.Length - 1; i++)
{
var pathKey = pathArr[i];
templateJson = templateJson[pathKey];
if (templateJson is null)
{
// The path is wrong. Do whatever validation is expected
}
else if (templateJson.Type == JTokenType.Array)
{
int index = item["index"].ToObject<int>(); // you may want to validate the index presence and format
templateJson = templateJson[index];
}
}
For approach with SelectToken you can either change your replacement json to match the format SelectToken understands or create a new path based on the index when it's not null:
var firstPropSplitter = path.IndexOf('.');
var firstProperty = path.Substring(0, firstPropSplitter);
var restPath = path.Substring(firstPropSplitter + 1);
var index = item["index"];
var newPath = $"{firstProperty}[{index}].{restPath}";

Related

Unable to find diff of two json properly in c#. Also getting error if there are 3 or more changes

The bounty expires in 14 hours. Answers to this question are eligible for a +100 reputation bounty.
DeLorean wants to draw more attention to this question.
I have two json string namely left(original json) and right(edited json). I want to find the difference between left and right and create a new json with only the changes and specific key "ID" AND "TYPE" . I tried using "JsonDiffPatchDotNet" and "JsonDiffer" packages in c# but no luck could someone help me out with this. Here is the code i have tried !
PACKAGES
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using JsonDiffPatchDotNet;
using JsonDiffer;
CODE MINIFY
public static string Minify(string json)
{
if (json == null)
{
throw new ArgumentNullException(nameof(json));
}
return Regex.Replace(json, "(\"(?:[^\"\\\\]|\\\\.)*\")|\\s+", "$1");
}
CODE MAIN LOGIC
public static void Main(string[] args)
{
string Tleft = #"{
""TEST JSON"" : ""JSON"",
""JSON"":{
""ANIMALS"":[
{
""ID"":0,
""TYPE"":""DOG"",
""DOG"":{
""TYPE"":""RETRIEVER"",
""RETRIEVER"":{
""NAME"":""LEO"",
""AGE"":3,
""YEARS"":[2019 , 2020, 2021],
""WEIGHTS"": [2,10,13]
}
},
""REMARKS"":{
""ID"":1,
""STATUS"":""GOOD"",
""REFERENCE"": {
""SOURCE"": ""XYZ"",
""FIT"": 1,
""BMI"" : 1
}
}
},
{
""ID"":1,
""TYPE"":""DOG2"",
""DOG2"":{
""TYPE"":""PUG"",
""RETRIEVER"":{
""NAME"":""HUTCH"",
""AGE"":4,
""YEARS"":[2019 , 2020, 2021, 2022],
""WEIGHTS"": [2,3,4,4]
}
},
""REMARKS"":{
""ID"":1,
""TYPE"" : ""REFERENCE"",
""STATUS"":""GOOD"",
""REFERENCE"": {
""SOURCE"": ""XYZ"",
""FIT"": 1,
""BMI"" : 1
}
}
},
{
""ID"": 2,
""TYPE"": ""DIAGNOSTICS"",
""STATUS"": ""ENABLED""
},
{
""ID"": 3,
""TYPE"": ""ORGANISATION"",
""ORGANISATION"":{
""NAME"":""RED CROSS"",
""YEAR"": 2023
}
}
]
}
}";
string Tright = #"{
""TEST JSON"" : ""JSON"",
""JSON"":{
""ANIMALS"":[
{
""ID"":0,
""TYPE"":""DOG"",
""DOG"":{
""TYPE"":""RETRIEVER"",
""RETRIEVER"":{
""NAME"":""LEO"",
""AGE"":3,
""YEARS"":[2019 , 2020, 2021],
""WEIGHTS"": [2,10,13]
}
},
""REMARKS"":{
""ID"":1,
""STATUS"":""GOOD"",
""REFERENCE"": {
""SOURCE"": ""XYZ"",
""FIT"": 1,
""BMI"" : 1
}
}
},
{
""ID"":1,
""TYPE"":""DOG2"",
""DOG2"":{
""TYPE"":""PUG"",
""RETRIEVER"":{
""NAME"":""HUTCH"",
""AGE"":4,
""YEARS"":[2019 , 2020, 2021, 2022],
""WEIGHTS"": [2,3,4,4]
}
},
""REMARKS"":{
""ID"":1,
""TYPE"" : ""REFERENCE"",
""STATUS"":""GOOD"",
""REFERENCE"": {
""SOURCE"": ""XYZ"",
""FIT"": 1,
""BMI"" : 1
}
}
},
{
""ID"": 2,
""TYPE"": ""DIAGNOSTICS"",
""STATUS"": ""ENABLED ZZZ""
},
{
""ID"": 3,
""TYPE"": ""ORGANISATION"",
""ORGANISATION"":{
""NAME"":""RED CROSS ZZZ"",
""YEAR"": 2023
}
}
]
}
}";
var left = JToken.Parse(Minify(Tleft));
var right = JToken.Parse(Minify(Tright));
var jdp = new JsonDiffPatch();
// difference using JsonDiffPatchDotNet package
var diff2 = JsonDifferentiator.Differentiate(left, right, OutputMode.Symbol, showOriginalValues: true);
Console.WriteLine("--------------------------------------");
Console.WriteLine("----diff using JsonDiffPatchDotNet----");
Console.WriteLine("--------------------------------------");
Console.WriteLine(diff2);
Console.WriteLine("***************************************");
Console.WriteLine("***************************************\n\n");
if (diff2 != null)
{
// removing * charecter from the difference
var formatedDiff = JToken.Parse(diff2.ToString().Replace("*", ""));
// patching with diff2
// Error occurs here if ther are more than 3 changes .
var Diff2PatchOutput = jdp.Patch(left, formatedDiff).ToString();
Console.WriteLine("--------------------------------------");
Console.WriteLine("---------- CURRENT OUTPUT --------");
Console.WriteLine("--------------------------------------");
Console.WriteLine(Diff2PatchOutput);
Console.WriteLine("***************************************");
Console.WriteLine("***************************************\n\n");
}
else
{
Console.WriteLine("Diff failed or Invalid json inputs left and rigth !");
}
}
CURRETN OUPUT
--------------------------------------
----diff using JsonDiffPatchDotNet----
--------------------------------------
{
"*JSON": {
"*ANIMALS": [
{
"*STATUS": "ENABLED ZZZ"
},
{
"*ORGANISATION": {
"*NAME": "RED CROSS ZZZ"
}
}
]
}
}
***************************************
***************************************
--------------------------------------
---------- CURRENT OUTPUT --------
--------------------------------------
{
"TEST JSON": "JSON",
"JSON": {
"ANIMALS": {
"ORGANISATION": {
"NAME": "RED CROSS ZZZ"
}
}
}
}
***************************************
***************************************
I'm trying to get the output like shown below. Any help would be really appreciated !
EXPECTED OUTPUT
--------------------------------------
---------- EXPECTED OUTPUT --------
--------------------------------------
{
"TEST JSON": "JSON",
"JSON": {
"ANIMALS": [
{
"ID": 2,
"TYPE": "DIAGNOSTICS",
"STATUS": "ENABLED ZZZ"
}
{
"ID": 3,
"TYPE": "ORGANISATION",
"ORGANISATION": {
"NAME": "RED CROSS ZZZ"
}
}
]
}
}
***************************************
***************************************
The problem is that if the changes are made in the same index of the JSON then they are being recognized but if they are made at 2 different indexes the first is being ignored. Also if there are changes at more than 2 different indexes it raises and error.
Error :
System.IO.InvalidDataException
HResult = 0x80131501
Message = Invalid patch object
Source = JsonDiffPatchDotNet
StackTrace:
at JsonDiffPatchDotNet.JsonDiffPatch.Patch(JToken left, JToken patch)
at JsonDiffPatchDotNet.JsonDiffPatch.ObjectPatch(JObject obj, JObject patch)
at JsonDiffPatchDotNet.JsonDiffPatch.Patch(JToken left, JToken patch)
at JsonDiffPatchDotNet.JsonDiffPatch.ObjectPatch(JObject obj, JObject patch)
at JsonDiffPatchDotNet.JsonDiffPatch.Patch(JToken left, JToken patch)
Please feel free to suggest any other approaches to achieve this 🙂.
Updated Answer
You're almost there - there's one thing you probably missed in my previous answer:
Actually, using two different libraries (where only one of them supports patching) gives no value here.
Meaning, you can really just use your JsonDiffPatch object, which has built-in patching support. So just re-introduce your diff1 of your previous question version, remove diff2, and that's it.
You'll end up with just those 3 lines (see full demo here):
var jdp = new JsonDiffPatch();
var diff1 = jdp.Diff(left, right);
// ...
var diff1PatchOutput = jdp.Patch(left, diff1).ToString();
And if you want to see just the changed items:
var diffAnimalsIds = ((JObject)JObject.Parse(diff1.ToString())["JSON"]["ANIMALS"]).Properties().Select(p => p.Name);
var allAnimals = JObject.Parse(diff1PatchOutput)["JSON"]["ANIMALS"].Cast<JObject>();
var changedAnimals = allAnimals.Where(animal => diffAnimalsIds.Contains(((JValue)animal["ID"]).ToString()));
Previous Answer
I'll start by saying that running your code (see full demo here) doesn't output what you mentioned in your question.
For example, the expected output can't actually be constructed by the provided jsons, beacuse STATUS and NAME are not the ones which have changes, but TYPE and YEAR. Visually, these are the changes:
That said, since I think that what got you those errors is mainly code disorganization, here are several points to help you resolve those issues.
[Don't be intimidated by all those bullets, I'm here to help!]
The first code block is wrong for several reasons:
// difference using JsonDiffer package
var diff1 = jdp.Diff(left, right);
// difference using JsonDiffPatchDotNet package
var diff2 = JsonDifferentiator.Differentiate(right, left...)
You've got:
The comments are the other way around (i.e. jdp.Diff is the one using the JsonDiffPatchDotNet package)
You're passing the json copies in two opposites ways (i.e. left, right and then right, left)
Actually, using two different libraries (where only one of them supports patching) gives no value here.
Then, the next code block actually digs the hole deeper:
var diff1PatchOutput = jdp.Patch(right, diff1).ToString();
// removing * charecter from the difference
var formatedDiff = JToken.Parse(diff2.ToString().Replace("*", ""));
// patching with diff2
// Error occurs here if ther are more than 3 changes .
var diff2PatchOutput = jdp.Patch(right, formatedDiff).ToString();
Because:
right and diff shouldn't really be patched together, because right is already "patched". You probably want left to be patched.
You shouldn't edit diff2, e.g. by removing *. If you want to know what's changed, construct the differ with something like JsonDifferentiator.Differentiate(left, right, OutputMode.Detailed, true), which will get you detailed object.
In the last line, as mentioned above, you probably don't want to patch right, as it's already "patched".
It seems like you are trying to use the JsonDiffPatchDotNet library to create a patch between two JSON objects and then use this patch to create a new JSON object with only the changes and specific keys. The approach you are using is correct, but you may need to modify it a bit to get the expected output.
One of the main issues with your code is that you are trying to create a new JSON object by applying the patch directly to the original JSON object. Instead, you need to create a new JSON object by selecting only the changed fields from the original JSON object and the patch.
Here is a modified version of your code that should work as expected:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static class JsonDiff {
public static JObject GetDiff(string originalJson, string editedJson) {
// Parse the original and edited JSON strings
var original = JObject.Parse(originalJson);
var edited = JObject.Parse(editedJson);
// Find the differences between the two
var diff = GetDifferences(original, edited);
// Extract the changes to an array of JObject representing the differences
var changes = new List<JObject>();
foreach (var d in diff) {
var change = new JObject();
change["ID"] = d.Key;
change["TYPE"] = d.Value.Type.ToString().ToUpper();
if (d.Value.Type == JTokenType.Object) {
change[d.Value.Path] = d.Value.ToString(Formatting.None);
} else {
change[d.Value.Path] = d.Value;
}
changes.Add(change);
}
// Build the result JSON object
var result = new JObject();
result["TEST JSON"] = "JSON";
result["JSON"] = new JObject();
result["JSON"]["ANIMALS"] = new JArray(changes);
return result;
}
private static IDictionary<string, JToken> GetDifferences(JToken original, JToken edited) {
var differences = new Dictionary<string, JToken>();
var originalProps = original.Children<JProperty>();
var editedProps = edited.Children<JProperty>();
// Find properties in original that are not in edited
foreach (var prop in originalProps) {
var editedProp = editedProps.FirstOrDefault(p => p.Name == prop.Name);
if (editedProp == null) {
differences.Add(prop.Path, prop.Value);
}
}
// Find properties in edited that are not in original
foreach (var prop in editedProps) {
var originalProp = originalProps.FirstOrDefault(p => p.Name == prop.Name);
if (originalProp == null) {
differences.Add(prop.Path, prop.Value);
}
}
// Find properties in original and edited that are not equal
foreach (var prop in originalProps) {
var editedProp = editedProps.FirstOrDefault(p => p.Name == prop.Name);
if (editedProp != null) {
if (!JToken.DeepEquals(prop.Value, editedProp.Value)) {
differences.Add(prop.Path, editedProp.Value);
}
}
}
return differences;
}
}

How to convert CSV string to json (or to c# object's list) without loop? using .NET Framework or .NET Core [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
Language: C#
.NET Framework 4.7 or .NET Core
I want to know is there any efficient way to convert plain string to JSON (or to C# object list)? Below is my string:
"Product","Date","Expiry","Type","Price":"ABC","20-Jul-2019","20-Jul-2022","Supplement","1300":"XYZ","20-Jul-2019","20-Jul-2022","Supplement","100":"AAA","20-Jul-2019","20-Jul-2022","Supplement","200":"XXX","20-Jul-2019","20-Jul-2022","Supplement","500"
If you notice, each row is separated by colon (:).
I can do it by iterating each line like split(':') then create DataTable then serialize it to JSON but I want to know is there any efficient way to avoid loop and improve performance?
Update
So far, I have achieved above by using below. It is working fine, but I am looking for someway to avoid the loops.
private static DataTable createDataTable(string[] csvArray)
{
DataTable dtCSV = new DataTable();
int idx = 0;
foreach (var str in csvArray)
{
var valueArray = str.Split(',');
if (idx == 0)
{
for (int i = 0; i < valueArray.Length; i++)
{
dtCSV.Columns.Add(valueArray[i].Replace("\"", ""), typeof(String));
}
idx++;
}
else
{
DataRow dr;
dr = dtCSV.NewRow();
for (int i = 0; i < valueArray.Length; i++)
{
dr[i] = valueArray[i].Replace("\"","");
}
dtCSV.Rows.Add(dr);
}
}
return dtCSV;
}
Just wondering, we can convert the string to csv using anchor tag's href using below code, is there a way to achieve similar in C# server side. Once it is converted to CSV then also it is easy to save in database table.
var csv = 'data:application/csv;charset=utf-8,' + encodeURIComponent(myString);
add above to href of anchor tag using JQuery. It export the data in CSV. But I don't want to export it, I want to save it to database.
This can be done using LINQ and Json.Net (but will still do loops internally) :
using Newtonsoft.Json;
using System.Linq;
// -----------
var data = "\"Product\",\"Date\",\"Expiry\",\"Type\",\"Price\":\"ABC\",\"20-Jul-2019\",\"20-Jul-2022\",\"Supplement\",\"1300\":\"XYZ\",\"20-Jul-2019\",\"20-Jul-2022\",\"Supplement\",\"100\":\"AAA\",\"20-Jul-2019\",\"20-Jul-2022\",\"Supplement\",\"200\":\"XXX\",\"20-Jul-2019\",\"20-Jul-2022\",\"Supplement\",\"500\"";
var datas = data.Split(':'); // string[] containing each line of the CSV
var MemberNames = datas[0].Split(','); // the first line, that contains the member names
var MYObj = datas.Skip(1) // don't take the first line (member names)
.Select((x) => x.Split(',') // split columns
/*
* create an anonymous collection
* with object having 2 properties Key and Value
* (and removes the unneeded ")
*/
.Select((y, i) => new { Key = MemberNames[i].Trim('"'),
Value = y.Trim('"') })
// convert it to a Dictionary
.ToDictionary(d => d.Key, d => d.Value));
// MYObject is IEnumerable<Dictionary<string, string>>
// serialize (remove indented if needed)
var Json = JsonConvert.SerializeObject(MYObj, Formatting.Indented);
Debug.WriteLine(Json);
This outputs :
[
{
"Product": "ABC",
"Date": "20-Jul-2019",
"Expiry": "20-Jul-2022",
"Type": "Supplement",
"Price": "1300"
},
{
"Product": "XYZ",
"Date": "20-Jul-2019",
"Expiry": "20-Jul-2022",
"Type": "Supplement",
"Price": "100"
},
{
"Product": "AAA",
"Date": "20-Jul-2019",
"Expiry": "20-Jul-2022",
"Type": "Supplement",
"Price": "200"
},
{
"Product": "XXX",
"Date": "20-Jul-2019",
"Expiry": "20-Jul-2022",
"Type": "Supplement",
"Price": "500"
}
]
First you must correct your string.
Somthing like {"Product":"ABC","Date":"20-Jul-2019",...}
then
return Json(new
{
data = "YourStringData",
});
put your string instead of YourStringData
It is very simple
like this:
string str="{\"name\":\"your name value\",\"age\":26}";
var jv= JsonValue.Parse(str);

Sorting JObject inside array by a field value

I have a JSON string like below:
{
"MetaData": {
"ResourcesUsed": 1
},
"Result": [
{
"locations": [
{
"country": "Papua New Guinea",
"city": "Jacquinot Bay",
"locTypeAttributes": {
"localDate": "2018-10-08T04:21:00-07:00",
"utcDate": "2018-10-08T04:21:00-07:00",
},
"point": {
"coordinates": [
151.52,
-5.6
],
"type": "Point"
}
},{
"country": "Papua New Guinea2",
"city": "Jacquinot Bay2",
"locTypeAttributes": {
"localDate": "2018-10-08T04:21:00-07:00",
"utcDate": "2018-10-02T04:21:00-07:00",
},
"point": {
"coordinates": [
151.52,
-5.6
],
"type": "Point"
}
}
]
}
]
}
I converted it to a JSON object using Newtonsoft. What I want to do is to sort the locations array(s) inside the Result array by the utcDate field nested in each locations item. I found the following thread: C# Sort JSON string keys. However, I could not still implement it since I have arrays inside my object, while that question deals purely with sorting objects inside objects alphabetically by property name.
Here is a piece of code that I wrote so far:
public string GenerateJson()
{
var model = (JObject)JsonConvert.DeserializeObject(data);
Sort(model);
}
private void Sort(JObject jObj)
{
var props = jObj["Result"][0]["locations"].ToList();
foreach (var prop in props)
{
prop.Remove();
}
foreach (var prop in props.OrderBy(p => p.Name))
{
jObj.Add(prop);
if (prop.Value is JObject)
Sort((JObject)prop.Value);
if (prop.Value is JArray)
{
Int32 iCount = prop.Value.Count();
for (Int32 iIterator = 0; iIterator < iCount; iIterator++)
if (prop.Value[iIterator] is JObject)
Sort((JObject)prop.Value[iIterator]);
}
}
}
You can sort each individual "Result[*].locations" array using LINQ as follows:
// Load the JSON without parsing or converting any dates.
var model = JsonConvert.DeserializeObject<JObject>(data, new JsonSerializerSettings{ DateParseHandling = DateParseHandling.None });
// Construct a serializer that converts all DateTime values to UTC
var serializer = JsonSerializer.CreateDefault(new JsonSerializerSettings{ DateTimeZoneHandling = DateTimeZoneHandling.Utc });
foreach (var locations in model.SelectTokens("Result[*].locations").OfType<JArray>())
{
// Then sort the locations by utcDate converting the value to UTC at this time.
var query = from location in locations
let utcDate = location.SelectToken("locTypeAttributes.utcDate").ToObject<DateTime>(serializer)
orderby utcDate
select location;
locations.ReplaceAll(query.ToList());
}
Notes:
The JSON is initially loaded using DateParseHandling.None to prevent the "localDate" and "utcDate" strings from being prematurely interpreted as DateTime objects with a uniform DateTime.Kind.
(For a discussion of how Json.NET interprets strings that look like dates, see Serializing Dates in JSON.)
We then iterate through all "locations" arrays using SelectTokens("Result[*].locations") where [*] is the JSONPath wildcard character, selecting all entries in the "Results" array.
We then order each "locations" array by deserializing the nested locTypeAttributes.utcDate to a UTC date, then ordering using LINQ.
Finally the array is updated using JArray.ReplaceAll().
If any locTypeAttributes.utcDate property is missing, an exception will be thrown. You could instead deserialize to DateTime? if that is a possibility.
Working sample .Net fiddle here.

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.

Is there any way of parsing a JSON string without deserialising it?

I am calling a web service that returns JSON with a duplicate node in some circumstances, providing output similar to this:
{
"shipments": [
{
"id": "A000001",
"name": "20141208 140652",
"type": "OUTLET",
"date": "2014-12-08 14:06:52",
"status": "SENT",
"received_at": null,
"created_at": "2014-12-08 14:06:52",
"updated_at": null,
"outlet_id": "SH000064"
},
{
"id": "A000002",
"name": "20141204 122650",
"type": "SUPPLIER",
"date": "2014-12-04 12:26:50",
"outlet_id": "SH000064",
"supplier_id": null,
"status": "RECEIVED",
"outlet_id": "SH000064",
"received_at": "2014-12-04 12:28:43",
"created_at": "2014-12-04 12:26:50",
"updated_at": "2014-12-04 12:28:43"
}
]
}
I am dependent on the provider of the service to fix this and this is not a priority for them so I have to deal with it. To handle this I am converting the JSON to XML, using the JsonReaderWriterFactory, and then removing the duplicate nodes from the resulting XML using the following routine:
protected virtual void RemoveDuplicateChildren(XmlNode node)
{
if (node.NodeType != XmlNodeType.Element || !node.HasChildNodes)
{
return;
}
var xNode = XElement.Load(node.CreateNavigator().ReadSubtree());
var duplicateNames = new List<string>();
foreach (XmlNode child in node.ChildNodes)
{
var isBottom = this.IsBottomElement(child); // Has no XmlNodeType.Element type children
if (!isBottom)
{
this.RemoveDuplicateChildren(child);
}
else
{
var count = xNode.Elements(child.Name).Count();
if (count > 1 && !duplicateNames.Contains(child.Name))
{
duplicateNames.Add(child.Name);
}
}
}
if (duplicateNames.Count > 0)
{
foreach (var duplicate in duplicateNames)
{
var nodeList = node.SelectNodes(duplicate);
if (nodeList.Count > 1)
{
for (int i=1; i<nodeList.Count; i++)
{
node.RemoveChild(nodeList[i]);
}
}
}
}
}
I now in a separate area need to use the DataContractJsonSerializer to deserialise the JSON to a strongly typed object, using the following code:
DataContractJsonSerializer serialiser = new DataContractJsonSerializer(typeof(ShipmentList));
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
var result = serialiser.ReadObject(stream);
This fails when the JSON contains that duplicate node, so I need to implement the same functionality as in the RemoveDuplicateChildren method but stepping through the JSON instead of an XML node, before the deserialisation. I can't use the quick-and-dirty option of using JsonConvert to convert to XML, removing the node with my existing method, then converting back to JSON because of the changes in the JSON that will result from the conversion to and from XML. Is there an equivalent way of navigating through a JSON hierarchy in C# as is provided by the XmlNode class?
UPDATE:
This question has become obfuscated by some of the comments. To clarify, the nodes I want to remove from the JSON are any nodes that are a repeat (by name, the content is irrelevant) at the same level of the same parent, such as the second "outlet_id" of the second "shipments" item in the example above. I need to do this in a generic way without hard coded element names. The RemoveDuplicateChildren method above does exactly what is needed, I'm just asking if there is a class I can use to do exactly the same as that method on a JSON string instead of an XML string.

Categories

Resources