I get this type of JSON data from Web API:
Data: {
FromDate: "2016-03-01",
Comment: "Bla bla",
...
},
FieldInfo: {
Items: [
{
Id: "FromDate",
Required: true,
DataType: "date"
},
{
Id: "Comment",
Required: true,
DataType: "string"
},
...
]
}
I need to serialize it to C# object and transform it to different representaion which should look something like this:
{
FieldInfo: {
Items: [
{
Id: "FromDate",
Required: true,
DataType: "date",
Value: "2016-03-01"
},
{
Id: "Comment",
Required: true,
DataType: "string"
Value: "Bla bla"
},
...
]
}
Basically map field values to its schema so it will not be separated.
Of course easiest way its just write a lot of if's for each field, which will not be very elegant solution and even not doable if we take into account that field schema and fields are dynamic so they can change. Property which I can rely on is Id in FieldInfo Item schema which should be a property name in Data object. One of the solution might be to use reflection in order to map Id value to property name in C# object representation. My question maybe there is another solution(s) to this problem or some tools which can help me in achieving this goal?
Using JSON.NET, you can parse the JSON into a JObject and manipulate it like so:
// Parse your json string into a JObject
JObject o = JObject.Parse(json);
// Retrieve the "Data" part of the json, i.e,
// the object that contains the values
var data = o["Data"];
// Retrieve the "FieldInfo" part. We will add values to this part.
var fieldInfo = o["FieldInfo"];
foreach (var token in fieldInfo["Items"])
{
var item = (JObject) token;
// Add the Value property to each item, get the value from the
// corresponding field
item["Value"] = data[(string)item["Id"]];
}
The variable fieldInfo will contain the information you wanted. Of course, it would be cleaner to create a new JObject to contain the information. But the example shows how you can retrieve and map the values you require.
You will have to implement some form of custom json converter to get the required output you desire to your new class structure. JSON.NET Custom JsonConverter:
http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm
Here is a nice beginners tutorial on how this is achieved:
http://geekswithblogs.net/DavidHoerster/archive/2011/07/26/json.net-custom-convertersndasha-quick-tour.aspx
Related
I'm not sure of the correct terminology as not overly knowledgeable about json. I have an existing json string/object of the following fixed format (i.e. all items are at the root level and of straightforward key/value pairs, although the number of items can vary):
{
"product_id": "1777",
"license_key": "ECHOES-SILENCE-PATIENCE-AND-GRACE",
"valid_for": "365",
"status": "active",
"times_activated_max": 1
}
I need a way to add a json string of varying schema/format that is passed into a method. This object can be of any json format. It's to be added to the root. For example adding this:
{
"name":"John",
"age":30,
"cars":["Ford", "BMW", "Fiat"]
}
..will become this:
{
"product_id": "1777",
"license_key": "ECHOES-SILENCE-PATIENCE-AND-GRACE",
"valid_for": "365",
"status": "active",
"times_activated_max": 1
"name":"John",
"age":30,
"cars":["Ford", "BMW", "Fiat"]
}
I have Newtonsoft.Json at my disposal for this.
I did read this but couldn't map it to my problem.
Hope someone can help
The JSON.NET documentation has an article just for that: Merging JSON. You can use JObject.Merge to merge two different objects into one:
JObject o1 = JObject.Parse(#"{
'FirstName': 'John',
'LastName': 'Smith',
'Enabled': false,
'Roles': [ 'User' ]
}");
JObject o2 = JObject.Parse(#"{
'Enabled': true,
'Roles': [ 'User', 'Admin' ]
}");
o1.Merge(o2, new JsonMergeSettings
{
// union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union
});
What you posted is two objects. A JSON document may be text, but what it contains are arrays and objects. "Merging" means finding a way to combine those objects/arrays and produce a new array or object.
Merge is defined by JContainer, the parent of both JObject and JArray. This means you can use Merge to merge both arrays and objects.
Another option with arrays is to use Enumerable.Union to combine the contents of both arrays, and create a new one :
var array1= JArray.Parse("[1,2,3]");
var array2= JArray.Parse("[3,4,5, \"a\"]");
var array3=new JArray(array1.Union(array2));
This returns [1,2,3,4,5,"a"]
Json.net uses the JObject type to represent arbitrary JSON objects. This has a handy Merge method for combining two JObjects.
Simply:
string s1 = #"
{
""product_id"": ""1777"",
""license_key"": ""ECHOES-SILENCE-PATIENCE-AND-GRACE"",
""valid_for"": ""365"",
""status"": ""active"",
""times_activated_max"": 1
}";
string s2 = #"
{
""name"":""John"",
""age"":30,
""cars"":[""Ford"", ""BMW"", ""Fiat""]
}";
JObject o1 = JObject.Parse(s1);
JObject o2 = JObject.Parse(s2);
o1.Merge(o2);
Console.WriteLine(o1);
See it here
Does anyone know how to convert the below nested JSON to CSV via CHOETL (An ETL framework for .NET)? Thank you!
I'm using this code but it will only return the first equipment record.
CODE:
{
using (var json = new ChoJSONReader("./test.json"))
{
csv.Write(json.Cast<dynamic>().Select(i => new
{
EquipmentId = i.GpsLocation.Equipment[0].EquipmentId,
InquiryValue = i.GpsLocation.Equipment[0].InquiryValue,
Timestamp = i.GpsLocation.Equipment[0].Timestamp
}));
}
}
JSON:
"GpsLocation": {
"Equipment": [
{
"EquipmentId": "EQ00001",
"InquiryValue": [
"IV00001"
],
"Timestamp": "2020-01-01 01:01:01.01",
},
{
"EquipmentId": "EQ00002",
"InquiryValue": [
"IV00002"
],
"Timestamp": "2020-01-01 01:01:01.01"
}
]
}
}````
As others suggest, the issue is you are only looking at the first element of the array.
It appears that the easiest way to control what you serialise into CSV is by correctly defining your source objects from JSON. JSON Path expressions come in pretty handy.
What I ended up doing here is query all JSON to return an array of Equipment objects regardless of where they are in the hierarchy (which means you may need to filter it a bit better depending on your full JSON).
Then it's pretty easy to define each field based on JSON path and just pass the result to CSVWriter.
Also check out some gotchas that I outlined in the respective comment lines.
void Main()
{
var jsonString = "{\"GpsLocation\":{\"Equipment\":[{\"EquipmentId\":\"EQ00001\",\"InquiryValue\":[\"IV00001\"],\"Timestamp\":\"2020-01-01 01:01:01.01\"},{\"EquipmentId\":\"EQ00002\",\"InquiryValue\":[\"IV00002\"],\"Timestamp\":\"2020-01-01 01:01:01.01\"}]}}";
var jsonReader = new StringReader(jsonString);
var csvWriter = new StringWriter(); // outputs to string, comment out if you want file output
//var csvWriter = new StreamWriter(".\\your_output.csv"); // writes to a file of your choice
using (var csv = new ChoCSVWriter(csvWriter))
using (var json = new ChoJSONReader(jsonReader)
.WithJSONPath("$..Equipment[*]", true) // firstly you scope the reader to all Equipment objects. take note of the second parameter. Apparently you need to pass true here as otherwise it just won't return anythig
.WithField("EquipmentId", jsonPath: "$.EquipmentId", isArray: false) // then you scope each field in the array to what you want it to be. Since you want scalar values, pass `isArray: false` for better predictability
.WithField("InquiryValue", jsonPath: "$.InquiryValue[0]", isArray: false) // since your InquiryValue is actually an array, you want to obtain first element here. if you don't do this, fields names and values would go askew
.WithField("Timestamp", jsonPath: "$.Timestamp", fieldType: typeof(DateTime), isArray: false)) // you can also supply field type, otherwise it seems to default to `string`
{
csv.WithFirstLineHeader().Write(json);
}
Console.WriteLine(csvWriter.GetStringBuilder().ToString()); // comment this out if writing to file - you won't need it
}
Update summary:
Pivoted to update the code to rely on JSON Path scoping - this seems to allow for field name manipulation with pretty low effort
Looking at your comment, you could probably simplify your file writer a little bit - use StreamWriter instead of StringWriter - see updated code for example
Here is the working sample of producing CSV from your JSON
string json = #"{
""GpsLocation"": {
""Equipment"": [
{
""EquipmentId"": ""EQ00001"",
""InquiryValue"": [
""IV00001""
],
""Timestamp"": ""2020-02-01 01:01:01.01"",
},
{
""EquipmentId"": ""EQ00002"",
""InquiryValue"": [
""IV00002""
],
""Timestamp"": ""2020-01-01 01:01:01.01""
}
]
}
}";
StringBuilder csv = new StringBuilder();
using (var r = ChoJSONReader.LoadText(json)
.WithJSONPath("$.GpsLocation.Equipment")
.WithField("EquipmentId")
.WithField("InquiryValue", jsonPath: "InquiryValue[0]", fieldType: typeof(string))
.WithField("Timestamp", fieldType: typeof(DateTime))
)
{
using (var w = new ChoCSVWriter(csv)
.WithFirstLineHeader())
w.Write(r);
}
Console.WriteLine(csv.ToString());
Output:
EquipmentId,InquiryValue,Timestamp
EQ00001,IV00001,2/1/2020 1:01:01 AM
EQ00002,IV00002,1/1/2020 1:01:01 AM
Sample fiddle: https://dotnetfiddle.net/hJWtqH
Your code is sound, but the issue is that you're only writing the first variable in the array by using i.GpsLocation.Equipment[0]. Instead, try looping over everything by putting it into a for loop, and changing the [0] to your iterating variable inside of said loop.
I am trying to get values with LINQ-TO-JSON in this JSON-TREE, So far it looks good until i've approached this categories element.
I want to get the value "Teknik" from this value and this is what i've tried to far without success.
Name = json["items"][i]["categories"].Children().Value<string>() ?? "Not assigned"
Name = json["items"][i]["categories"][0].Value<string>() ?? "Not assigned"
Name = json["items"][i]["categories"].First.Value<string>()
But none of these results works for me. Please help!
You didn't post the actual JSON string and the "tree" doesn't help. The JSON string in Querying JSON with LINQ could be represented with a similar tree, so I'll assume that's your source data:
string json = #"{
'channel': {
'title': 'James Newton-King',
'link': 'http://james.newtonking.com',
'description': 'James Newton-King\'s blog.',
'item': [
{
'title': 'Json.NET 1.3 + New license + Now on CodePlex',
'description': 'Annoucing the release of Json.NET 1.3, the MIT license and the source on CodePlex',
'link': 'http://james.newtonking.com/projects/json-net.aspx',
'categories': [
'Json.NET',
'CodePlex'
]
},
{
'title': 'LINQ to JSON beta',
'description': 'Annoucing LINQ to JSON',
'link': 'http://james.newtonking.com/projects/json-net.aspx',
'categories': [
'Json.NET',
'LINQ'
]
}
]
}
}";
JObject rss = JObject.Parse(json);
The example shows two ways to access values, through indexers or LINQ queries.
Using indexers, you'd only need [0] to access the first category of an item, eg:
rss["items"][i]["categories"][0];
That returns a JValue object as a JObject. You can cast it directly to string to get its value, eg:
(string)rss["items"][i]["categories"][0]
Or to a JValue, if you want to handle it as a JSON object and read the Value property, eg:
(rss["items"][i]["categories"][0] as JValue).Value;
A LINQ query wouldn't need indexers. If you wanted to retrieve the Category names you could write :
var query= from item in rss["channel"]["item"]
from category in item["categories"]
select new {Name=(string)category};
Or
var query = from category in rss["channel"]["item"].SelectMany(item=>item["categories"])
select new {Name=(string)category};
Or
var query = rss["channel"]["item"].SelectMany(item=>item["categories"])
.Select(category=>new {Name=(string)category});
So I have a JSON string that I am passing from an AJAX call to my controller. I have a list of indexed values that I am passing into a dynamic object.
I deserialize the JSON with
JsonConvert.DeserializeObject<dynamic>(s)
This is the output from that dynamic object:
"RolePermissions[0].RolePermissionId": "269",
"RolePermissions[0].HasAccess": "false",
"RolePermissions[1].RolePermissionId": "270",
"RolePermissions[1].HasAccess": "false",
"RolePermissions[2].RolePermissionId": "271",
"RolePermissions[2].HasAccess": "true",
"RolePermissions[3].RolePermissionId": "272",
"RolePermissions[3].HasAccess": "false"
When I try to access the a property of the object with
ssObj.RolePermissions[0].RolePermissionId
I get a RuntimeBinderException. I have tried to use JObject.Parse, which works great, but for some reason, the values in the array become out of order.
Any help would be greatly appreciated. Thanks!
When you try to do RolePermissions[0].RolePermissionId you are trying to access a nested collection containing an object with a property RolePermissionId at index 0. But your JSON doesn't represent a hierarchy of objects, it represents a single flat object with key/value pairs whose keys contain periods and brackets. Since c# identifiers don't allow such characters so you have no way to access such property values using dynamic directly.
Instead, your options include:
Take advantage of the fact that JsonConvert.DeserializeObject<dynamic>(s) actually returns a JObject and use its dictionary indexer:
var ssObj = JsonConvert.DeserializeObject<dynamic>(s);
var rolePermissionId = (string)ssObj["RolePermissions[0].RolePermissionId"];
If you prefer a slightly more typed solution, you could deserialize to a Dictionary<string, dynamic>:
var ssDict = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(s);
var rolePermissionId = (string)ssDict["RolePermissions[0].RolePermissionId"];
Or for a much more statically typed solution, parse explicitly to a JObject and use LINQ to JSON:
var jObj = JObject.Parse(s);
var rolePermissionId = (string)jObj["RolePermissions[0].RolePermissionId"];
Sample fiddle showing the various options.
If you are in control of the data being sent via AJAX then make sure the data sent is properly formatted.
In order to be able to deserialize variable s like:
var ssObj = JsonConvert.DeserializeObject<dynamic>(s);
and access the resulting object in this manner:
ssObj.RolePermissions[0].RolePermissionId
then the JSON value in s, based on your sample and desired behavior, would have to look like this:
{
"RolePermissions": [
{
"RolePermissionId": "269",
"HasAccess": "false"
},
{
"RolePermissionId": "270",
"HasAccess": "false"
},
{
"RolePermissionId": "271",
"HasAccess": "true"
},
{
"RolePermissionId": "272",
"HasAccess": "false"
}
]
}
This quick unit test showed that it is possible to get indexed values when deserializing JSON into a dynamic object
[TestClass]
public class UnitTest1 {
[TestMethod]
public void GetIndexedValuesWhenDeserializingJSONIntoDynamicObject() {
var s = #"
{
'RolePermissions': [
{
'RolePermissionId': '269',
'HasAccess': 'false'
},
{
'RolePermissionId': '270',
'HasAccess': 'false'
},
{
'RolePermissionId': '271',
'HasAccess': 'true'
},
{
'RolePermissionId': '272',
'HasAccess': 'false'
}
]
}
";
var ssObj = JsonConvert.DeserializeObject<dynamic>(s);
var result = ssObj.RolePermissions[0].RolePermissionId;
Assert.AreEqual("269", (string)result);
}
}
So you need to make sure you are sending well formatted JSON to your controller to achieve desired behavior.
I have following json created after I sereliaze a dictionary
{"Results":["{\"BaseCurrency\":\"USD\",\"TermCurrency\":\"JPY\"}","{\"BaseCurrency\":\"USD\",\"TermCurrency\":\"JPY\"}","{\"BaseCurrency\":\"USD\",\"TermCurrency\":\"JPY\"}","{\"BaseCurrency\":\"USD\",\"TermCurrency\":\"JPY\"}"],"Total":4}
When I try to load it into the extjs store , the store is not loaded
var store = Ext.create('Ext.data.Store', {
fields: fields,
pageSize: itemsPerPage,
proxy: {
type: 'ajax',
url: getDataWithPageURL,
reader: {
type: 'json',
root: 'Results',
totalProperty: 'Total'
}
}
});
But if I remove slash hard coded and it's working
{"Results":["{"BaseCurrency":"USD","TermCurrency":"JPY"}","{"BaseCurrency":"USD","TermCurrency":"JPY"}","{"BaseCurrency":"USD","TermCurrency":"JPY"}","{"BaseCurrency":"USD"}"],"Total":4}
I create json using Newtonsoft.Json
Dictionary<string, object> dict = new Dictionary<string, object>();
string s = JsonConvert.SerializeObject(dist);
How can I remove slash in server side in order to produce valid json for extjs store.
I tried
result = result.Replace("\"","'");
And
result =result.Replace("\"", #"""")
It's not working
Clearly, Ext doesn't like that you have encoded a json object as a string and then put them in another json object. You have two options that I see.
See if you can return the dict on the server-side without first converting it into a string. Some code, somewhere, is taking your string and putting it in a json object with 'Results' and 'Total'. Check to see if that code can take a Dictionary as-is.
Unwrap the 'Results' propery on the client-side. One way would be to make your own reader:
Ext.define('MyReader', {
extend: 'Ext.data.reader.Json',
alias: 'reader.my-json',
read: function(object) {
object.Results = Ext.Array.map(object.Results, Ext.decode);
return this.callParent([object]);
}
});
Then use type: 'my-json' in your reader config.
Here is my test case:
var data = {"Results":["{\"BaseCurrency\":\"USD\",\"TermCurrency\":\"JPY\"}","{\"BaseCurrency\":\"USD\",\"TermCurrency\":\"JPY\"}","{\"BaseCurrency\":\"USD\",\"TermCurrency\":\"JPY\"}","{\"BaseCurrency\":\"USD\",\"TermCurrency\":\"JPY\"}"],"Total":4};
Ext.define('Currency', {
extend: 'Ext.data.Model',
fields: [
{ name: 'BaseCurrency', type: 'string' },
{ name: 'TermCurrency', type: 'string' }
]
});
Ext.define('MyReader', {
extend: 'Ext.data.reader.Json',
alias: 'reader.my-json',
read: function(object) {
object.Results = Ext.Array.map(object.Results, Ext.decode);
return this.callParent([object]);
}
});
var store = Ext.create('Ext.data.Store', {
model: 'Currency',
data: data,
proxy: {
type: 'memory',
reader: {
type: 'my-json',
root: 'Results',
totalProperty: 'Total'
}
}
});