How do I change the CourseName from History to Gym if the ID is equally to 2?
{
"Result": {
"StudentInfo": {
"ID": 20,
"Name": "Bob",
"IgnoreThis": [
{
"ID": 123,
"Something":"abc"
}
]
},
"Courses": [
{
"ID": 1,
"CourseName":"Math"
},
{
"ID": 2,
"CourseName":"History"
}
]
}
}
This code below is just a fantasy code to show what I had in mind:
{ "Result":{ "Course" : [ if id=2 inside "Courses" then "CourseName":"Gym" ] }}
I will be using Postman.
Here is a solution that works (it could possibly be tidied up); it does however use Newtonsoft.Json (a well-known nuget package).
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string blah = "{'Result': { 'StudentInfo': { 'ID': 20, 'Name': 'Bob', 'IgnoreThis': [{'ID': 123,'Something':'abc'}]}, 'Courses': [{'ID': 1,'CourseName':'Math'},{'ID': 2,'CourseName':'History'}]}}";
dynamic json = JsonConvert.DeserializeObject(blah);
var dynamicJson = json.Result.Courses; // included to show how dynamic could be accessed instead
JObject arr = json;
foreach (var course in arr["Result"]["Courses"].Where(x => x["ID"].Value<int>() == 2))
{
course["CourseName"] = "Gym";
}
var newResult = json.ToString();
}
}
}
Related
I now need to deserialize a JSON that looks like this:
{
"arguments": {
"game": [
"--username",
"--version",
"--assetsDir",
{
"rules": [
{
"action": "allow",
"features": {
"is_demo_user": true
}
}
],
"value": "--demo"
},
{
"rules": [
{
"action": "allow",
"features": {
"has_custom_resolution": true
}
}
],
"value": [
"--width",
"--height"
]
}
]
}
}
As you can see, the array named "game" has both "value" and "object" in it. (But the fact is WORSE than this example, the number of elements is NOT certain)
And the data type of arguments.game[*].value is NOT certain, too.
I used to use classes to describe it, but deserialization failed.
Can't seem to describe an array with multiple element types with a class?
I am using Json.NET. Is there any way to deserialize this "game" array.
Thanks.
Is it a requirement to deserialize to an instance of a class? You could use an ExpandoObject:
using System.Dynamic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
Console.WriteLine("Hello, World!");
string json = #"{
""arguments"": {
""game"": [
""--username"",
""--version"",
""--assetsDir"",
{
""rules"": [
{
""action"": ""allow"",
""features"": {
""is_demo_user"": true
}
}
],
""value"": ""--demo""
},
{
""rules"": [
{
""action"": ""allow"",
""features"": {
""has_custom_resolution"": true
}
}
],
""value"": [
""--width"",
""--height""
]
}
]
}
}";
var expConverter = new ExpandoObjectConverter();
dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(json, expConverter);
The obj variable will contain the result of the JSON conversion, then you can traverse the dynamic object in code.
For example, to get a list of strings under 'game':
IList<object> list = new List<object>(obj.arguments.game);
foreach (object str in list)
{
if (str as string != null)
{
Console.WriteLine(str as string);
}
}
I have a doubt if it is possible to serialize a collection of BsonDocument results as a JSON pair of key objects.
For example, I attach a piece of code that creates a collection of BsonDocument with and _id and name as fields,
using MongoDB.Bson;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ExampleBsonDocumentSerializeToJsonAsArray
{
class Program
{
static void Main(string[] args)
{
BsonDocument[] data = new BsonDocument[]{
new BsonDocument {
{ "_id" , "1" },
{ "name" , "name_1" },
{ "description" , "description_1" },
}
,new BsonDocument {
{ "_id" , "2" },
{ "name" , "name_2" },
{ "description" , "description_2" },
}
,new BsonDocument {
{ "_id" , "3" },
{ "name" , "name_3" },
{ "description" , "description_3" },
}
};
Console.WriteLine(data.ToJson());
}
}
}
List 1.1
The piece from the list 1.1 shows it gives output as a JSON array of objects:
[{
"_id": "1",
"name": "name_1",
"description": "description_1"
}, {
"_id": "2",
"name": "name_2",
"description": "description_2"
}, {
"_id": "3",
"name": "name_3",
"description": "description_3"
}]
Having the field '_id' as the key of the collection, I would like to serialize it as a set of key-object JSON instead of an array of objects. The result of serialized JSON should be like this,
{
"1": {
"name": "name_1"
, "description": "description_1"
},
"2": {
"name": "name_2"
, "description": "description_2"
},
"3": {
"name": "name_3"
, "description": "description_3"
}
}
I don't know whether is this possible or not.
You can convert BsonDocument to Dictionary via System.Linq.
using System.Linq;
var kvp = data.AsEnumerable()
.ToDictionary(x => x["_id"], x => new { name = x["name"], description = x["description"] });
Console.WriteLine(kvp.ToJson());
Sample program
When I run the below code (C#) as a custom activity (compiled .dll is added to a nuget package and triggered in a UiPath sequence with some user defined variables/arguments. I am able to retrieve the name of the variable and it's type, but I cannot find the proper syntax to retrieve the value (I just want to convert it to a String, no need to do anything fancy). I can access some of the properties, so i know i am close. I have done my best to read the docs, but in this instance, it may be a little two abstract for me. I have gone through many interations and scoured as much of the internet as I can, but I cannot seem to figure it out.
using Newtonsoft.Json.Linq;
using System;
using System.Activities;
using System.Activities.Hosting;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
namespace AutoLog
{
public sealed class GetRootActivity : NativeActivity
{
public OutArgument<string> variables { get; set; }
protected override void Execute(NativeActivityContext context)
{
this.variables.Set((ActivityContext)context, Library.getLocalVariables(context));
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
base.CacheMetadata(metadata);
metadata.AddDefaultExtensionProvider<GetRootActivity.WorkflowInstanceInfo>((Func<GetRootActivity.WorkflowInstanceInfo>)(() => new GetRootActivity.WorkflowInstanceInfo()));
}
public class Library
{
public static string getLocalVariables(NativeActivityContext context)
{
var properties = context.DataContext.GetProperties();
JArray variables = new JArray();
foreach(PropertyDescriptor p in properties)
{
JObject variable = new JObject();
variable["name"] = p.Name;
variable["type"] = p.PropertyType.ToString();
string string_value = "";
try
{
var myValue = context.DataContext.GetType().GetProperty(p.Name).GetValue(context.DataContext, null);
string_value = myValue.ToString();
}
catch(Exception e)
{
}
variable["value"] = string_value;
variables.Add(variable);
}
return variables.ToString();
}
}
}
}
Below is an example of the JSON it generates, as you can see, the "value" field is empty
[
{
"name": "a",
"type": "System.String",
"value": ""
},
{
"name": "b",
"type": "System.Boolean",
"value": ""
},
{
"name": "c",
"type": "System.Int32",
"value": ""
},
{
"name": "test",
"type": "System.String",
"value": ""
},
{
"name": "f",
"type": "System.String",
"value": ""
}
]
var myValue = context.DataContext.GetType().GetProperty(p.Name).GetValue(context.DataContext, null);
string_value = myValue.ToString();
can be changed to
string_value = p.GetValue(context.DataContext) as String;
I had previously tried this approach but got trying to make the cast dynamic, and apparently I had never tried the simpler solution.
I've got a JSON stream coming back from a server, and I need to search for a specific value of the node "ID" using JSON.net to parse the data.
And I can almost make it work, but not quite because the results coming back are deeply nested in each other -- this is due to the fact that I'm getting a folder structure back. I've boiled the JSON down to a much simpler version. I'm getting this:
{
"data": {
"id": 0,
"name": "",
"childFolders": [{
"id": 19002,
"name": "Locker",
"childFolders": [{
"id": 19003,
"name": "Folder1",
"childFolders": [],
"childComponents": [{
"id": 19005,
"name": "route1",
"state": "STOPPED",
"type": "ROUTE"
}]
}, {
"id": 19004,
"name": "Folder2",
"childFolders": [],
"childComponents": [{
"id": 19008,
"name": "comm1",
"state": "STOPPED",
"type": "COMMUNICATION_POINT"
}, {
"id": 19006,
"name": "route2",
"state": "STOPPED",
"type": "ROUTE"
}, {
"id": 19007,
"name": "route3",
"state": "STOPPED",
"type": "ROUTE"
}]
}],
"childComponents": []
}],
"childComponents": []
},
"error": null
}
I can almost get there by going:
var objects = JObject.Parse(results);
var subobjects = objects["data"]["childFolders"][0]["childFolders"][1];
I can see in the debug view that it'll parse the object, but won't let me search within.
My ultimate goal is to be able to search for "route3" and get back 19007, since that's the ID for that route. I've found some results, but all of them assume you know how far nested the object is. The object I'm searching for could be 2 deep or 20 deep.
My ultimate goal is to be able to search for "route3" and get back 19007
You can use linq and Descendants method of JObject to do it:
var dirs = JObject.Parse(json)
.Descendants()
.Where(x=>x is JObject)
.Where(x=>x["id"]!=null && x["name"]!=null)
.Select(x =>new { ID= (int)x["id"], Name = (string)x["name"] })
.ToList();
var id = dirs.Find(x => x.Name == "route3").ID;
You can use the SelectToken or SelectTokens functions to provide a JPath to search for your desired node. Here is an example that would provide you the route based on name:
JObject.Parse(jsonData)["data"].SelectToken("$..childComponents[?(#.name=='route3')]")
You can find more documentation on JPath here
Simply write a recursive function:
private Thing FindThing(Thing thing, string name)
{
if (thing.name == name)
return thing;
foreach (var subThing in thing.childFolders.Concat(thing.childComponents))
{
var foundSub = FindThing(subThing, name);
if (foundSub != null)
return foundSub;
}
return null;
}
class RootObject
{
public Thing data { get; set; }
}
class Thing
{
public int id { get; set; }
public string name { get; set; }
public List<Thing> childFolders { get; set; } = new List<Thing>();
public List<Thing> childComponents { get; set; } = new List<Thing>();
}
And using it:
var obj = JsonConvert.DeserializeObject<RootObject>(jsonString);
var result = FindThing(obj.data, "route3");
How can I create a JsonArray with a child data object array? I am using Web service and C#.
I want the result of the JsonArray to look like the following:
[{
"name": "Deadpool",
"url": {
"small": "http://api.android.info/images/small/deadpool.jpg",
"medium": "http://api.android.info/images/medium/deadpool.jpg",
"large": "http://api.android.info/images/large/deadpool.jpg"
},
"time": "February 12, 2016"
},
{
"name": "The Jungle Book",
"url": {
"small": "http://api.android.info/images/small/book.jpg",
"medium": "http://api.android.info/images/medium/book.jpg",
"large": "http://api.android.info/images/large/book.jpg"
},
"time": "April 15, 2016"
},
{
"name": "X-Men: Apocalypse",
"url": {
"small": "http://api.android.info/images/small/xmen.jpg",
"medium": "http://api.android.info/images/medium/xmen.jpg",
"large": "http://api.android.info/images/large/xmen.jpg"
},
"time": "May 27, 2016"
}]
First, create the models that can output the given data. You need a MovieModel, a movie can have multiple image sizes and urls stored, we use a dictionary for this.
UPDATED
MovieModel.cs
public class MovieModel
{
public string Name { get; set; }
public Dictionary<string,string> Url { get; set; }
public string Time { get; set; }
}
Now you need to install Newtonsoft.Json from Nuget packages. Then import it.
using Newtonsoft.Json;
Initialize the model and convert to Json using SerializeObject() method.
var movieList = new List<MovieModel>
{
new MovieModel
{
MovieName = "Deadpool",
Time = DateTime.UtcNow.ToString("t"),
Url = new Dictionary<string, string>
{
{ "small", "http://api.android.info/images/small/deadpool.jpg" },
{ "medium", "http://api.android.info/images/medium/deadpool.jpg" }
}
}
// .. add more movies .. //
};
// convert to camelcase and set indentation
var output = JsonConvert.SerializeObject(
movieList,
Formatting.Indented,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
}
);
// testing output on console
Console.WriteLine(output);
In a real application, you would create Movie instances by getting data from a database, not initializing it for yourself as used in this example.