Serializing object using json.net using lambda selector - c#

I have the following code
class MyJSONSerializableClass
{
[JsonProperty("index")]
public int Index { get; set; };
[JsonIgnore]
public long Id { get; set; };
}
var collection = new List<MyJSONSerializableClass>()
{
new MyJSONSerializableClass()
{
Index = 10,
Id = 1000
}
};
string jsonOutput = JsonConvert.SerializeObject(collection);
jsonOutput would be
[{ index: 10 }]
I would like jsonOutput to be
[ 10 ]
Is there any class attribute I can apply to MyJSONSerializableClass that would tell JSON.NET to emit only the property Index, instead of the entire object?
Something along the lines of
[JsonOutput(f => f.Index)]
class MyJSONSerializableClass
{
...
}
Thanks

Not sure about the attributes, but alternatively, you could try
JsonConvert.SerializeObject(collection.Select(o => o.Index)).
As per documentation, all IEnumerable's will be arrays.

Related

Convert json string from one type to another with .net core c#

I am struggling to convert below input json to output json as this is what is the required format to call
hubspot api to submit a form. I am writing this using .net core within Azure function.
Input Json
{
"Email":"myemail#test.com",
"Phone":"12345678",
"Address":"address 1"
}
Output json
{
"fields": [
{
"name": "Email",
"value": "myemail#test.com"
},
{
"name": "Phone",
"value": "12345678"
},
{
"name": "Address",
"value": "address 1"
}
]
}
I converted the input json to dictionary using
IDictionary<string, string> dictionary = JsonConvert.DeserializeObject<IDictionary<string, string>>(inputJson);
but that gives me key value pair instead of name value pair.
I would like the output as detailed above.
Any help/example code would be highly appreciated.
You could create your own "NameValuePair" class/struct if you don't want "Key" as the field name:
public class FieldContainer
{
[JsonProperty("fields")]
public IEnumerable<NameValuePair> Fields { get; set; }
}
public struct NameValuePair
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("value")]
public string Value { get; set; }
public NameValuePair(string name, string value)
{
Name = name;
Value = value;
}
}
And then do like you've already done, but converting the KeyValuePairs into your own struct:
var inJson = #"{
""Email"":""myemail#test.com"",
""Phone"":""12345678"",
""Address"":""address 1""
}";
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(inJson);
var container = new FieldContainer
{
Fields = dict.Select(pair => new NameValuePair(pair.Key, pair.Value))
};
var outJson = JsonConvert.SerializeObject(container);
See this fiddle for a demonstration.
Easiest way to do this would be to take the json and convert it to Dictionary<string, string>. Loop over each KeyValuePair and create a list of Fields using LINQ. Once you have the List of fields, create your RootObject.
public class RootObject
{
[JsonProperty("fields")]
public List<Field> Fields { get; set; }
}
public class Field
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("value")]
public string Value { get; set; }
}
// Create a dictionary
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonStr);
// Create a list of Fields
List<Field> fields = dict.Select(x => new Field() { Name = x.Key, Value = x.Value }).ToList();
// Create the final Object.
RootObject rootObj = JsonConvert.SerializeObject(new RootObject() { Fields = fields });
Alternative solution, using JObject.Parse() to parse the original JSON, then iterate its Properties to create an array of JObjects with different names and values.
The resulting IEnumerable<JObject> is then converted to a JArray, used to create the final fields object.
var jObj = JObject.Parse(json);
var newObjects = jObj.Properties().Select(p => new JObject {
new JProperty("name", p.Name),
new JProperty("value", p.Value)});
var fields = new JObject() {
{ "fields", JArray.FromObject(newObjects)}
};
Console.WriteLine(fields);

Compare JSON array and JSON object: Cannot convert Array to String error

I am trying to compare json value and based on that i want to update the existing value,for example, currently we have "value" : [r0] in json, i want to compare and if value : [r0] ,then update it to [r0,r1] but iam hiting error that it cannot compare and there is a cast issue, could someone suggest what could be done
public void updateJsonParameter(string file)
{
try
{
var list = new List<string> { "joe", "test" };
JArray array = new JArray(list);
var jobject = JObject.Parse(file);
var ringvalue = (string)jobject["properties"]["parameters"]["ringValue"]["value"]; // unable to case here and compare
jobject["properties"]["parameters"]["ringValue"]["value"] = array; // able to update value but i want to update after comparing the existing values
var result = JsonConvert.SerializeObject(jobject);
}
following is the json format
{
"properties": {
"displayName": "jayatestdefid",
"description": "test assignment through API",
"metadata": {
"assignedBy": "xyz#gmail.com"
},
"policyDefinitionId": "/providers/Microsoft.Management/managementgroups/MGTest/providers/Microsoft.Authorization/policyDefinitions/test",
"parameters": {
"ringValue": {
"value": ["r0"]
}
},
"enforcementMode": "DoNotEnforce",
}
}
jobject.properties.parameters.ringValue.value is an array ["r0"] with one element "r0". If you want to check if it's an array with one element and that element is "r0", do exactly that:
var ringvalue = jobject["properties"]["parameters"]["ringValue"]["value"];
if(ringvalue.length == 1 && ringvalue[0] == "r0")
jobject["properties"]["parameters"]["ringValue"]["value"] = array;
You could compare the ringvalue (which is an JArray) using JArray.DeepEquals and then replace if the comparison returns true. For example,
var list = new List<string> { "joe", "test" };
JArray array = new JArray(list);
JArray valueToCompare = new JArray(new[]{"r0"});
var ringvalue = (JArray)jobject["properties"]["parameters"]["ringValue"]["value"];
if(JArray.DeepEquals(ringvalue,valueToCompare))
{
jobject["properties"]["parameters"]["ringValue"]["value"] = array;
}
First, as Klaycon said in his answer, it's worth noting that your "value" is not a single string. In json, whenever you see [ and ] then you have a collection, or an array, or a list.
When I work with json strings, I always like to be able to convert them into a strongly typed object. There is a very handy online tool I use all the time: http://json2csharp.com/
I took your json string that you provided and pasted it into that website. Here is that your object(s) look like when converted into c# classes:
public class RootObject // You can name this whatever you want
{
public Properties properties { get; set; }
}
public class Metadata
{
public string assignedBy { get; set; }
}
public class RingValue
{
public List<string> value { get; set; }
}
public class Parameters
{
public RingValue ringValue { get; set; }
}
public class Properties
{
public string displayName { get; set; }
public string description { get; set; }
public Metadata metadata { get; set; }
public string policyDefinitionId { get; set; }
public Parameters parameters { get; set; }
public string enforcementMode { get; set; }
}
Now, we can easily do the logic you need as follows:
// This is your json string, escaped and turned into a single string:
string file = "{ \"properties\": { \"displayName\": \"jayatestdefid\", \"description\": \"test assignment through API\", \"metadata\": { \"assignedBy\": \"xyz#gmail.com\" }, \"policyDefinitionId\": \"/providers/Microsoft.Management/managementgroups/MGTest/providers/Microsoft.Authorization/policyDefinitions/test\", \"parameters\": { \"ringValue\": { \"value\": [\"r0\"] } }, \"enforcementMode\": \"DoNotEnforce\", }}";
// Convert your json string into an instance of the RootObject class
RootObject jobject = JsonConvert.DeserializeObject<RootObject>(file);
// Get a list of all the "values"
List<string> values = jobject.properties.parameters.ringValue.value;
// Loop over your colleciton of "value" and do your logic
for (int i = 0; i < values.Count; ++i)
{
if (values[i] == "r0")
{
values[i] = "r0,r1";
}
}
// And finally, turn your object back into a json string
var result = JsonConvert.SerializeObject(jobject);
And this is the final result:
{
"properties":{
"displayName":"jayatestdefid",
"description":"test assignment through API",
"metadata":{
"assignedBy":"xyz#gmail.com"
},
"policyDefinitionId":"/providers/Microsoft.Management/managementgroups/MGTest/providers/Microsoft.Authorization/policyDefinitions/test",
"parameters":{
"ringValue":{
"value":[
"r0,r1"
]
}
},
"enforcementMode":"DoNotEnforce"
}
}

Using AutoMapper, Json.NET, or other library to update parent and children properties with changes that are in a json string

I have a scenario where I am given a json string that ONLY contains the properties that have been modified for a given parent / children object by the user.
When given this string, I need to load the original object and update it with the changes that are in the json string.
Sample classes
public class ParentObject
{
public int ID { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<ChildObject> Children { get; set; }
}
public class ChildObject
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
}
Here is the "original" object.
var originalObject = new ParentObject
{
ID = 1,
Prop1 = "Prop 1 Original",
Prop2 = "Prop 2 Original",
Children = new List<ChildObject>() { new ChildObject {
ID = 22,
PropA = "Prop A Original"}
}
};
Here is the Json string with the "modified" object.
string modifiedProperties =
#"[{
""ID"": 1,
""Prop1"": ""Prop 1 Changed"",
""Children"": [{
""ID"": 22,
""PropA"": ""Prop A Changed""
}]
}]";
I must then "update" the "original" object with the properties that changed.
I could use reflection to iterate through the properties of the "original" object and then compare those with the Json string BUT I was wonder if there is some other library like AutoMapper that can do this easier?
Newton Json.NET merge is looking promising.
https://www.newtonsoft.com/json/help/html/MergeJson.htm
AutoMapper code -- Mostly working
This code "ALMOST" works in that it does map the parent and child BUT it fails if the child has a property with the same name as the parent.
AKA the ID field is not mapping on the children.
// Configure the mapping
Mapper.Initialize(cfg => cfg.CreateMap<ExpandoObject, ParentObject>());
// Convert the JSon into a dynamic list
var sources = JsonConvert.DeserializeObject<List<dynamic>>(modifiedProperties);
// Iterate the list of changes
foreach (dynamic source in sources)
{
// Get the source ID to pull the record from the database
var sourceID = Convert.ToInt32(source.ID);
// Simulate a DB call to get the original object
var destination = originalObject;
// Map the objects together
var realDestination = Mapper.Map(source, destination);
// ** OUTPUT **
// realDestination.ID = 1 <-- THIS IS CORRECT
// realDestination.Prop1 = "Prop 1 Changed" <-- THIS IS CORRECT
// realDestination.Prop2 = "Prop 2 Original" <-- THIS IS CORRECT
// realDestination.Children.FirstOrDefault().ID = 0 <-- THIS SHOULD BE 22 NOT 0
// realDestination.Children.FirstOrDefault().PropA = "Prop A Changed" <-- THIS IS CORRECT
// realDestination.Children.FirstOrDefault().PropB = null <-- THIS IS CORRECT
}
I am still working on the json merge option.
Any other suggestion are welcome.
I got both AutoMapper and Json.NET to work.
Here is the code I am using along with test data.
public class ParentObject
{
public int ID { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<ChildObject> Children { get; set; }
}
public class ChildObject
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
}
// Original object before modifications
var originalObject = new ParentObject
{
ID = 1,
Prop1 = "Prop 1 Original",
Prop2 = "Prop 2 Original",
Children = new List<ChildObject>() { new ChildObject {
ID = 22,
PropA = "Prop A Original"}
}
};
// Get the modified properties
string modifiedProperties =
#"[{
""ID"": 1,
""Prop1"": ""Prop 1 Changed"",
""Children"": [{
""ID"": 22,
""PropA"": ""Prop A Changed""
}]
}]";
/* AUTOMAPPER CODE */
// Convert the JSon into a dynamic list for AUTOMAPPER
var mapperSources = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ExpandoObject>>(modifiedProperties);
// Initialize mapper
AutoMapper.Mapper.Initialize(cfg => { });
// Iterate the list of changes for AUTOMAPPER
foreach (dynamic source in mapperSources)
{
// Get the source ID to pull the record from the database
var sourceID = Convert.ToInt32(source.ID);
// Simulate a DB call to get the original object
var destination = originalObject;
// Map the objects together
var realDestination = AutoMapper.Mapper.Map(source, destination);
}
/* JSON.NET CODE */
// Convert the JSon into a dynamic list for JSON.NET
Newtonsoft.Json.Linq.JArray sources = Newtonsoft.Json.Linq.JArray.Parse(modifiedProperties);
// Iterate the list of changes for JSON.NET
foreach (Newtonsoft.Json.Linq.JObject source in sources)
{
// Get the source ID to pull the record from the database
var sourceID = Convert.ToInt32(source["ID"]);
// Simulate a DB call to get the original object
var destination = originalObject;
// convert the object into a JObject for merge
Newtonsoft.Json.Linq.JObject tempDestination = Newtonsoft.Json.Linq.JObject.FromObject(destination);
// Map the objects together
tempDestination.Merge(source, new Newtonsoft.Json.Linq.JsonMergeSettings
{
// Using merge
MergeArrayHandling = Newtonsoft.Json.Linq.MergeArrayHandling.Merge
});
var realDestination = tempDestination.ToObject<ParentObject>();
}
/* EXPECTED OUTPUT
realDestination.ID = 1
realDestination.Prop1 = "Prop 1 Changed"
realDestination.Prop2 = "Prop 2 Original"
realDestination.Children.FirstOrDefault().ID = 22
realDestination.Children.FirstOrDefault().PropA = "Prop A Changed"
realDestination.Children.FirstOrDefault().PropB = null
*/
I ran a .NET Stopwatch and here are the "performance" results as best I know how to measure.
If anyone notices an error in my test code please let me know.
Stopwatch sw = new Stopwatch();
for (int c = 0; c < 4; c++)
{
sw.Restart();
for (int i = 0; i < 500000; i++)
{
/* MAPPING CODE HERE */
}
sw.Stop();
Console.WriteLine("Elapsed = {0}", sw.Elapsed);
}
/* STOPWATCH RESULTS
Elapsed AUTOMAPPER = 00:00:16.5727406
Elapsed AUTOMAPPER = 00:00:16.8015508
Elapsed AUTOMAPPER = 00:00:14.6642556
Elapsed AUTOMAPPER = 00:00:13.9644693
Elapsed JSON.NET = 00:00:18.8097409
Elapsed JSON.NET = 00:00:17.6313278
Elapsed JSON.NET = 00:00:18.6343426
Elapsed JSON.NET = 00:00:18.8855448
*/

Convert CSV (nested objects) to JSON

I need to convert a CSV data (with one line of header, and one line of data) in a JSON object.
The CSV contains nested columns, there is an example:
id,name,category/id,category/name,category/subcategory/id,category/subcategory/name,description
0,Test123,15,Cat123,10,SubCat123,Desc123
And I want a JSON to look like this:
{
"id": 0,
"name": "Test123",
"category": {
"id": 15,
"name": "Cat123",
"subcategory": {
"id": 10,
"name": "SubCat123",
}
},
"description": "Desc123"
}
I've tried CsvHelper and ChoETL libs, without success, because these libs, as far as I know, require that I have a class as model, but I don't have these classes, because the data is totally dynamic.
The site http://www.convertcsv.com/csv-to-json.htm is a good example to make this with success.
Just paste the JSON that I have created above, go to Step 3 and check the option 'Recreate nested objects and arrays', then click 'CSV to JSON' in step 5.
But I need to make this in my application, without using external frameworks.
How can I make this to work?
If you do not have it, add the newtonsoft library(dll) and then add the folowing references
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Add the following classes
public class Rootobject
{
public int id { get; set; }
public string name { get; set; }
public Category category { get; set; }
public string description { get; set; }
}
public class Category
{
public int id { get; set; }
public string name { get; set; }
public Subcategory subcategory { get; set; }
}
public class Subcategory
{
public int id { get; set; }
public string name { get; set; }
}
then use this code
DataTable CSVData = new DataTable(); // your csv rows
HashSet<Rootobject> MyObjectsList = new HashSet<Rootobject>(); //create hashset to handle your classes
foreach(DataRow row in CSVData.Rows)
{
//change the indices in ItemArray with the right indices
MyObjectsList.Add(new Rootobject() {
id = (int)row.ItemArray[0], name = (string)row.ItemArray[0], category = new Category() {
id = (int)row.ItemArray[0], name = (string)row.ItemArray[0], subcategory = new Subcategory() {
id = (int)row.ItemArray[0], name = (string)row.ItemArray[0]
}
}
});
}
string _ResultObj = JsonConvert.SerializeObject(MyObjectsList); //here get your json string
With the latest ChoETL.JSON 1.0.1.6, you can convert them easily. This will work only if the headers are simple texts, no spaces or special characters allowed.
using (var json = new ChoJSONWriter("nested.json")
.SingleElement()
.Configure(c => c.SupportMultipleContent = true)
.Configure(c => c.DefaultArrayHandling = false)
)
{
using (var csv = new ChoCSVReader("nested.csv").WithFirstLineHeader())
json.Write(csv.Select(i => i.ConvertToNestedObject('/')));
}
Output JSON will look at below
[
{
"id":0,
"name":"Test123",
"category": {
"id": 15,
"name": "Cat123",
"subcategory": {
"id": 10,
"name": "SubCat123"
}
},
"description":"Desc123"
}
]
Sample fiddle: https://dotnetfiddle.net/vttMIB
UPDATE:
Cinchoo ETL now supports native nested object support by just setting 'NestedColumnSeparator' configuration parameter to '/'. Sample below shows how to
using (var json = new ChoJSONWriter("nested.json")
.SingleElement()
.Configure(c => c.SupportMultipleContent = true)
.Configure(c => c.DefaultArrayHandling = false)
)
{
using (var csv = new ChoCSVReader("nested.csv")
.WithFirstLineHeader()
.Configure(c => c.NestedColumnSeparator = '/')
)
json.Write(csv);
}
Sample fiddle: https://dotnetfiddle.net/xFlQso

c# JSON Directly Nested Arrays

I'm hitting a RESTful api and trying to process the returned data. The data itself consists of json arrays within arrays.
{
"category1": [
[
"http://url1",
"URL 1 Title"
],
[
"http://url2",
"URL 2 Title"
]
],
"category2": [
[
"http://url3",
"URL 3 Title"
]
],
"category3": [
[
"http://url4",
"URL 4 Title"
]
]
}
I've created a class that maps to the content above, but I have no idea of how to parse the result and extract the individual URL and URL Titles.
I can see how I might do it if these were objects, but no idea on how to access directly nested arrays.
Try this
class Output
{
public string[][] category1 { get; set; }
public string[][] category2 { get; set; }
public string[][] category3 { get; set; }
}
class OutputCategory
{
public string uri { get; set; }
public string label { get; set; }
}
I have modified your code in the main() method to the following one.
string samplejson =
#"{'category1':[['http://url1','URL 1 Title'],['http://url2','URL 2 Title']],'category2':[['http://url3','URL 3 Title']],'category3':[['http://url4','URL 4 Title']]}";
//deserialised to my class object
var result = JsonConvert.DeserializeObject<Output>(samplejson);
//create new output object
Output outCat = new Output();
//I now want to assign the appropriate values to my new output object
Console.WriteLine(result.category2.Length);
Console.WriteLine("URL: " + result.category1[0][0]);
Console.WriteLine("Title: " + result.category1[0][1]);
Make sure you check the length of the array and then proceed with your result.
I am using this in my codes, after you have YourModel class created then you can use the bellow approach
JsonConvert.DeserializeObject<YourModel>(content,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
You want to have jagged arrays:
public class Model
{
public string[][] category1 { get; set; }
public string[][] category2 { get; set; }
public string[][] category3 { get; set; }
}
And then:
var model = JsonConvert.DeserializeObject<Model>(json);
foreach (var element in model.category1)
{
var url = element[0];
var title = element[1];
}

Categories

Resources