I'm trying to convert a nested json to simple json by recursively traversing.
(Structure of input json is unknown)
for example, I want json like this
{
"FirstName": "Rahul",
"LastName": "B",
"EmpType": {
"RID": 2,
"Title": "Full Time"
},
"CTC": "3.5",
"Exp": "1",
"ComplexObj": {
"RID": 3,
"Title": {
"Test": "RID",
"TWO": {
"Test": 12
}
}
}
}
to be converted something like this
{
"FirstName": "Rahul",
"LastName": "B",
"EmpType__RID": 2,
"EmpType__Title": "Full Time",
"CTC": "3.5",
"Exp": "1",
"ComplexObj__RID": 3,
"ComplexObj__Title__Test": "RID",
"ComplexObj__Title__TWO__Test": 12
}
each fields in nested object will be changed to key which represents its actual path.
this is what I have done so far.
public static void ConvertNestedJsonToSimpleJson(JObject jobject, ref JObject jobjectRef, string currentNodeName = "", string rootPath = "")
{
string propName = "";
if (currentNodeName.Equals(rootPath))
{
propName = currentNodeName;
}
else
{
propName = (rootPath == "" && currentNodeName == "") ? rootPath + "" + currentNodeName : rootPath + "__" + currentNodeName;
}
foreach (JProperty jprop in jobject.Properties())
{
if (jprop.Children<JObject>().Count() == 0)
{
jobjectRef.Add(propName == "" ? jprop.Name : propName + "__" + jprop.Name, jprop.Value);
}
else
{
currentNodeName = jprop.Name;
rootPath = rootPath == "" ? jprop.Name : rootPath;
ConvertNestedJsonToSimpleJson(JObject.Parse(jprop.Value.ToString()), ref jobjectRef, currentNodeName, rootPath);
}
}
}
and getting wrong result
{
"FirstName": "Rahul",
"LastName": "B",
"EmpType__RID": 2,
"EmpType__Title": "Full Time",
"CTC": "3.5",
"Exp": "1",
"EmpType__ComplexObj__RID": 3,
"EmpType__Title__Test": "RID",
"EmpType__two__Test": 12
}
will appreciate any help on correcting my code, or any other approach to archive this.
You don't need to convert the value of the property to string and then parse it again every time - just cast it to JObject
You don't need the complicated conditional logic to generate the name of the property - just use this: prefix + jprop.Name + "__"
The code:
public static void FlattenJson(JObject node, JObject result, string prefix = "")
{
foreach (var jprop in node.Properties())
{
if (jprop.Children<JObject>().Count() == 0)
{
result.Add(prefix + jprop.Name, jprop.Value);
}
else
{
FlattenJson((JObject)jprop.Value, $"{prefix}{jprop.Name}__", result);
}
}
}
You can call it like this:
var node = JObject.Parse(/* the input string */);
var result = new JObject();
FlattenJson(node, result);
Your problem is in the line rootPath = rootPath == "" ? jprop.Name : rootPath;. You are changing the rootPath when you first come across EmpType which means when you process ComplexObj your rootPath is wrong. What I believe you intended is just to change what you were passing into the recursive function.
As it is though it is unnecessary to keep track of root and currentnode as two separate items. Better would be to just track the current prefix for a given node in code that looks more like this:
public static void ConvertNestedJsonToSimpleJson(JObject input, JObject output, string prefix = "")
{
foreach (JProperty jprop in input.Properties())
{
var name = prefix==""?jprop.Name:String.Format("{0}__{1}", prefix,jprop.Name);
if (jprop.Children<JObject>().Count() == 0)
{
output.Add(name, jprop.Value);
}
else
{
ConvertNestedJsonToSimpleJson((JObject)jprop.Value, output, name);
}
}
}
This now gives me the output:
{
"FirstName": "Rahul",
"LastName": "B",
"EmpType__RID": 2,
"EmpType__Title": "Full Time",
"CTC": "3.5",
"Exp": "1",
"ComplexObj__RID": 3,
"ComplexObj__Title__Test": "RID",
"ComplexObj__Title__TWO__Test": 12
}
which looks correct.
Could you do something like this using linq
var jsonObj = jobject.select(x => new CustomJson {
FirstName = x.FirstName,
LastName = x.LastName,
EmpTypeId = x.EmpType.Id,
Title = x.EmpType.Title
etc etc
});
Related
I used Kendo Grid in ASP.NET MVC and I created a custom server side filter like this:
public string CreateString(IEnumerable<IFilterDescriptor> filters, string defaultFilter)
{
StringBuilder builder = new StringBuilder();
if (filters == null || filters.Count() == 0)
{
_filterstring = defaultFilter;
return _filterstring;
}
if (filters.Any())
{
foreach (Kendo.Mvc.FilterDescriptor filter in filters)
{
string template = Templates[filter.Operator.ToString().ToLower()];
builder.AppendFormat(template, filter.Member, filter.Value);
}
}
_filterstring = builder.ToString();
return _filterstring;
}
private static readonly IDictionary<string, string> Templates = new Dictionary<string, string>
{
{ "eq", "{0} = '{1}'" },
{ "neq", "{0} <> '{1}'" },
{ "lt", "{0} < '{1}'" },
{ "lte", "{0} <= '{1}'" },
{ "gt", "{0} > '{1}'" },
{ "gte", "{0} >= '{1}'" },
{ "startswith", "{0} like '{1}*'" },
{ "endswith", "{0} like '*{1}'" },
{ "contains", "{0} like '*{1}*'" },
{ "doesnotcontain", "{0} not like '*{1}*'" }
};
That returns a string like this:
Title like '*a*'
And I used Linq.Dynamic and I filter the data sets like:
var articleCategories = _articleCategories.AsQueryable();
if (!string.IsNullOrEmpty(filter))
articleCategories = articleCategories.Where(filter).ToList();
But it returns this error:
Expression of type 'Boolean' expected.
I am not really good with those, but you might be lacking a logical and between you filters? I added one in your code, but I am not really good with this. Tell me if it works.
if (filters == null || filters.Count() == 0)
{
_filterstring = defaultFilter;
return _filterstring;
}
if (filters.Any())
{
string template = Templates[filters[0].Operator.ToString().ToLower()];
builder.AppendFormat(template, filter.Member, filter.Value);
if (filters.Count() > 1) {
foreach (Kendo.Mvc.FilterDescriptor filter in filters.Skip(1))
{
template = " && " + Templates[filter.Operator.ToString().ToLower()];
builder.AppendFormat(template, filter.Member, filter.Value);
}
}
}
In my application I have a List<Users>
[{
"Id": "1",
"Name": "last, first",
"Skills": [{"SN":"20","SL":"12"},{"SN":"197","SL":"09"}]
},
{
"Id": "2",
"Name": "Black, Jack",
"Skills": [{"SN":"40","SL":"07"},{"SN":"199","SL":"05"}]
},
{
"Id": "3",
"Name": "Rooney, Wayne",
"Skills": [{"SN":"40","SL":"11"},{"SN":"201","SL":"07"}]
}]
I need to convert this data into one string in a format like this:
"1/last, first/20-12,197-09;2/Black, Jack/40-07,199-05;3/Rooney,
Wayne/40-11,201-07"
I tried to serialize this object to get a JSON string:
public IHttpActionResult ImportUsers([FromBody]IEnumerable<Users> newUsers)
{
string agents = JsonConvert.SerializeObject(newUsers);
return Ok(agents);
}
and get a result:
"[{\"Id\":\"1\",\"Name\":\"last,
first\",\"Skills\":[{\"SN\":\"20\",\"SL\":\"12\"},{\"SN\":\"197\",\"SL\":\"09\"}]},{\"Id\":\"2\",\"Name\":\"Black,
Jack\",\"Skills\":[{\"SN\":\"40\",\"SL\":\"07\"},{\"SN\":\"199\",\"SL\":\"05\"}]},{\"Id\":\"3\",\"Name\":\"Rooney,
Wayne\",\"Skills\":[{\"SN\":\"40\",\"SL\":\"11\"},{\"SN\":\"201\",\"SL\":\"07\"}]}]"
Than I tried to deserialize this string to get values for each variables
var dict = JsonConvert.DeserializeObject<dynamic>(agents);
List<string> results = new List<string>();
foreach (var d in dict)
{
var skills = d["Skills"];
string skill = string.Join(",", skills);
string list = d["Id"] + "/" + d["Name"] + "/" + skill;
results.Add(list);
}
string result = string.Join(";", results);
and get this string:
"1/last, first/{\r\n \"SN\": \"20\",\r\n \"SL\": \"12\"\r\n},{\r\n
\"SN\": \"197\",\r\n \"SL\": \"09\"\r\n};2/Black, Jack/{\r\n \"SN\":
\"40\",\r\n \"SL\": \"07\"\r\n},{\r\n \"SN\": \"199\",\r\n \"SL\":
\"05\"\r\n};3/Rooney, Wayne/{\r\n \"SN\": \"40\",\r\n \"SL\":
\"11\"\r\n},{\r\n \"SN\": \"201\",\r\n \"SL\": \"07\"\r\n}"
So it's more like what I need, but I still have a problem with values in Skills list. How can I get values from it like it was with Id and Name columns? Maybe I need to deserialize this part one more time or use some regular expressions to achieve the string that I need?
Your expected result is not a valid json. Then the better way is to format result string via c# without 3rd party libraries.
Laconic way is to use string functiions:
public IHttpActionResult ImportUsers([FromBody] IEnumerable<User> newUsers)
{
var agents = string.Join(";", newUsers.Select(GetFormattedUserString));
return Ok(agents);
}
private string GetFormattedUserString(User user)
{
return string.Concat(user.Id, "/", user.Name, "/", string.Join(",", user.Skills.Select(skill => string.Concat(skill.SN, "-", skill.SL))));
}
Efficient and less understandable (for my opinion) way is to use StringBuilder:
public IHttpActionResult ImportUsers([FromBody] IEnumerable<User> newUsers)
{
var sb = new StringBuilder();
foreach (var user in newUsers)
{
sb.Append(user.Id);
sb.Append("/");
sb.Append(user.Name);
sb.Append("/");
for (int i = 0; i < user.Skills.Length; i++)
{
sb.Append(user.Skills[i].SN);
sb.Append("-");
sb.Append(user.Skills[i].SL);
if (i != user.Skills.Length)
sb.Append(",");
}
sb.Append(";");
}
var result = sb.ToString();
return Ok(result);
}
You can slightly modify your code to make it work. In order to get the skills in the way you want, you can nest another foreach loop that will create a list of skills in the requested format:
var dict = JsonConvert.DeserializeObject<dynamic>(agents);
List<string> results = new List<string>();
foreach (var d in dict)
{
List<string> skills = new List<string>();
foreach (var s in d["Skills"])
{
skills.Add(s["SN"].ToString() + "-" + s["SL"].ToString());
}
string skill = string.Join(",", skills);
string list = d["Id"] + "/" + d["Name"] + "/" + skill;
results.Add(list);
}
string result = string.Join(";", results);
On the WMI query below if the proptery is blank then my code errors. How can i make the code return "" when the property does not exist ? For example queryObj["Help Telephone"] is null so my code is erroring out but i want it to continue.
ManagementObjectSearcher searcherSoftware = new ManagementObjectSearcher("root\\CIMV2", "Select * from Win32_Product");
foreach (ManagementObject queryObj in searcherSoftware.Get())
{
ItemsUnderControlObject TemporarySoftware = new ItemsUnderControlObject();
TemporarySoftware.sType = "Software";
TemporarySoftware.sAssignmentType = queryObj["AssignmentType"].ToString().Split(new[] { "Name=" }, StringSplitOptions.None).Last().Trim('"'); //http://stackoverflow.com/questions/22583873/get-names-from-string-values/22583919?noredirect=1#22583919
TemporarySoftware.sCaption = queryObj["Caption"].ToString().Split(new[] { "Name=" }, StringSplitOptions.None).Last().Trim('"');
TemporarySoftware.sDescription = queryObj["Description"].ToString().Split(new[] { "Name=" }, StringSplitOptions.None).Last().Trim('"');
TemporarySoftware.sHelpLink = queryObj["HelpLink"].ToString().Split(new[] { "Name=" }, StringSplitOptions.None).Last().Trim('"');
TemporarySoftware.sHelpTelephone = queryObj["Help Telephone"].ToString().Split(new[] { "Name=" }, StringSplitOptions.None).Last().Trim('"');
}
You could create a helper method:
private static string GetNameContents(ManagementObject queryObj, string propertyName)
{
object propertyValue = queryObj[propertyName];
if (propertyValue == null)
return String.Empty;
string propertyString = propertyValue.ToString();
return propertyString.Length == 0
? String.Empty
: propertyString.Split(new[] { "Name=" }, StringSplitOptions.None).Last().Trim('"');
}
And use it as follows:
TemporarySoftware.sAssignmentType = GetNameContents(queryObj, "AssignmentType");
TemporarySoftware.sCaption = GetNameContents(queryObj, "Caption");
Ok, so I am trying to send POST commands over an http connection, and using JSON formatting to do so. I am writing the program to do this in C#, and was wondering how I would format an array of values to be passed as JSON to the server.
Currently I have this:
new {name = "command" , index = "X", optional = "0"}
Which translates to this in JSON:
"name": "command",
"index": "X",
"optional": "0"
And I want to make an array, called items, where each element contains these three values. So it would essentially be an array of objects, in which the object contains a name, an index, and an optional field.
My guess was that it would be something along the lines of this:
new {items = [(name = "command" , index = "X", optional = "0"),
(name = "status" , index = "X", optional = "0")]}
Which, if it were correct syntax, would translate to this in JSON:
"items":
[
{
"name": "command",
"index": "X",
"optional": "0"
},
{
"name": "status",
"index": "X",
"optional": "0"
}
]
But, evidently I'm doing it wrong. Ideas? Any help is appreciated.
You're close. This should do the trick:
new {items = new [] {
new {name = "command" , index = "X", optional = "0"},
new {name = "command" , index = "X", optional = "0"}
}}
If your source was an enumerable of some sort, you might want to do this:
new {items = source.Select(item => new
{
name = item.Name, index = item.Index, options = item.Optional
})};
You'd better create some class for each item instead of using anonymous objects. And in object you're serializing you should have array of those items. E.g.:
public class Item
{
public string name { get; set; }
public string index { get; set; }
public string optional { get; set; }
}
public class RootObject
{
public List<Item> items { get; set; }
}
Usage:
var objectToSerialize = new RootObject();
objectToSerialize.items = new List<Item>
{
new Item { name = "test1", index = "index1" },
new Item { name = "test2", index = "index2" }
};
And in the result you won't have to change things several times if you need to change data-structure.
p.s. Here's very nice tool for complex jsons
Also , with Anonymous types ( I prefer not to do this) -- this is just another approach.
void Main()
{
var x = new
{
items = new[]
{
new
{
name = "command", index = "X", optional = "0"
},
new
{
name = "command", index = "X", optional = "0"
}
}
};
JavaScriptSerializer js = new JavaScriptSerializer(); //system.web.extension assembly....
Console.WriteLine(js.Serialize(x));
}
result :
{"items":[{"name":"command","index":"X","optional":"0"},{"name":"command","index":"X","optional":"0"}]}
new {var_data[counter] =new [] {
new{ "S NO": "+ obj_Data_Row["F_ID_ITEM_MASTER"].ToString() +","PART NAME": " + obj_Data_Row["F_PART_NAME"].ToString() + ","PART ID": " + obj_Data_Row["F_PART_ID"].ToString() + ","PART CODE":" + obj_Data_Row["F_PART_CODE"].ToString() + ", "CIENT PART ID": " + obj_Data_Row["F_ID_CLIENT"].ToString() + ","TYPES":" + obj_Data_Row["F_TYPE"].ToString() + ","UOM":" + obj_Data_Row["F_UOM"].ToString() + ","SPECIFICATION":" + obj_Data_Row["F_SPECIFICATION"].ToString() + ","MODEL":" + obj_Data_Row["F_MODEL"].ToString() + ","LOCATION":" + obj_Data_Row["F_LOCATION"].ToString() + ","STD WEIGHT":" + obj_Data_Row["F_STD_WEIGHT"].ToString() + ","THICKNESS":" + obj_Data_Row["F_THICKNESS"].ToString() + ","WIDTH":" + obj_Data_Row["F_WIDTH"].ToString() + ","HEIGHT":" + obj_Data_Row["F_HEIGHT"].ToString() + ","STUFF QUALITY":" + obj_Data_Row["F_STUFF_QTY"].ToString() + ","FREIGHT":" + obj_Data_Row["F_FREIGHT"].ToString() + ","THRESHOLD FG":" + obj_Data_Row["F_THRESHOLD_FG"].ToString() + ","THRESHOLD CL STOCK":" + obj_Data_Row["F_THRESHOLD_CL_STOCK"].ToString() + ","DESCRIPTION":" + obj_Data_Row["F_DESCRIPTION"].ToString() + "}
}
};
I need to store a set of values which I get from my C# code to a javascript array.
Im getting an error .. Can someone tell what the error is ?
Im using this jcode.
$.get('Dataextract.aspx', function (data, textStatus) {
alert('Status is ' + textStatus);//success
alert('JSON data string is: ' + data);//string as below
var JSONdata = data;
eval(JSONdata);//error here-> expected ;
alert(JSONdata.rowval[0].CustomerID);
}, 'text');
I am using an ajax query to retrieve an array of JSON object. the data objects value that i get is something like this as string.
{"rowval" :[{"CustomerID":12"Title":"Mr.""FirstName":"Johnny""MiddleName":"A.""LastName":"Caprio""CompanyName":"Bikes and Motorbikes""RowNumber":10},{"CustomerID":16"Title":"Mr.""FirstName":"Christopher""MiddleName":"R.""LastName":"Beck""CompanyName":"Bulk Discount Store""RowNumber":11},{"CustomerID":18"Title":"Mr.""FirstName":"David""MiddleName":"J.""LastName":"Liu""CompanyName":"Catalog Store""RowNumber":12},{"CustomerID":19"Title":"Mr.""FirstName":"John""MiddleName":"A.""LastName":"Beaver""CompanyName":"Center Cycle Shop""RowNumber":13},{"CustomerID":20"Title":"Ms.""FirstName":"Jean""MiddleName":"P.""LastName":"Handley""CompanyName":"Central Discount Store""RowNumber":14},{"CustomerID":21"Title":"FirstName":"Jinghao""MiddleName":"LastName":"Liu""CompanyName":"Chic Department Stores""RowNumber":15},{"CustomerID":22"Title":"Ms.""FirstName":"Linda""MiddleName":"E.""LastName":"Burnett""CompanyName":"Travel Systems""RowNumber":16},{"CustomerID":23"Title":"Mr.""FirstName":"Kerim""MiddleName":"LastName":"Hanif""CompanyName":"Bike World""RowNumber":17},{"CustomerID":24"Title":"Mr.""FirstName":"Kevin""MiddleName":"LastName":"Liu""CompanyName":"Eastside Department Store""RowNumber":18},{"CustomerID":25"Title":"Mr.""FirstName":"Donald""MiddleName":"L.""LastName":"Blanton""CompanyName":"Coalition Bike Company""RowNumber":19},{"CustomerID":28"Title":"Ms.""FirstName":"Jackie""MiddleName":"E.""LastName":"Blackwell""CompanyName":"Commuter Bicycle Store""RowNumber":20}]}
Here is my C# code that is generating the JSON
sb.Append("{\"rowval\" :");
sb.Append("[");
if (table != null)
{
foreach (DataRow row in table.Rows)
{
sb.Append("{");
if (row.Table != null && row.Table.Columns != null && row.Table.Columns.Count > 0)
{
foreach (DataColumn column in row.Table.Columns)
{
parseMember(row[column], column.ColumnName, sb);
}
}
sb.Append("},");
}
}
sb.Append("]");
sb.Append("}");
sqlcon.Close();
Response.Write(sb);
}
private static void parseMember(object val, string memberName, StringBuilder sb)
{
Type t = val.GetType();
if (memberName != null && memberName.Trim().Length > 0)
sb.AppendFormat("\"{0}\":", memberName);
if (typeof(string) == t || typeof(char) == t)
sb.AppendFormat("\"{0}\"", val.ToString());
else
sb.AppendFormat("{0}", val.ToString());
}
if you are getting json then specifying dataType equal to json as 4th argument in$.getwill parse the json which you can iterate using theeach` method of jquery, like
$.get('Dataextract.aspx', function (data, textStatus) {
alert('Status is ' + textStatus);//success
alert('JSON data string is: ' + data);//string as below
// no need for eval
// var JSONdata = data;
// eval(JSONdata);//error here-> expected ;
alert(JSONdata.rowval[0].CustomerID);
}, "json"); // <--
or you can parse the json explicitly like
$.get('Dataextract.aspx', function (data, textStatus) {
alert('Status is ' + textStatus);//success
alert('JSON data string is: ' + data);//string as below
var JSONdata = $.parseJSON(data);
// eval(JSONdata);//error here-> expected ; again no need for the eval
alert(JSONdata.rowval[0].CustomerID);
}, 'text');
update
the json you are forming is not correct, for validating your json you can visit www.jsonlint.com, this is the valid json
{
"rowval": [
{
"CustomerID": 12, // <-- you are missing the commas
"Title": "Mr.",
"FirstName": "Johnny",
"MiddleName": "A.",
"LastName": "Caprio",
"CompanyName": "Bikes and Motorbikes",
"RowNumber": 10
}
]
}
You need two classes -call them RoVal and Customer, for example- and define them as so:
class RowVal
{
public List<Customer> Customers {get;set;}
}
class Customer
{
public int ID {get;set;}
public string Title{get;set;}
public string FirstName {get;set;}
//and so on
}
Now you can deserialize that JSON string as so
JavascriptSerializer serializer = new JavascriptSerializer();
RowVal rowVal = serializer.Deserialize<RowVal>(yourJSonstring);