I have been up for hours trying to figure this out. There seem to be a lot of posts tip toe'ing around the issue I am having but none of the posts seem to work for me.
I very simply want to deserialize the $ref field in my json document using json.net
Example JSON:
{
"items": [
{ "$ref": "#/parameters/RequestId" },
{
"name": "user",
"in": "body",
"description": "User metadata object",
"required": true
}
],
"parameters": {
"RequestId": {
"name": "X-requestId",
"in": "header",
"description": "this is a request id",
"required": false
}
}
}
Example Classes:
public class Item
{
public string Name {get;set;}
public string In {get;set;}
public string Description {get;set;}
public bool Required {get;set;}
}
public class RootDoc
{
public List<Item> Items {get;set;}
public Dictionary<string, Item> Parameters {get;set;}
}
What I am expecting:
var doc = JsonConvert.DeserializeObject<RootDoc>(json, new JsonSerializerSettings()
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
Error = delegate (object sender, ErrorEventArgs args)
{
// Handle all errors
args.ErrorContext.Handled = true;
}
});
Assert.IsNotNull(doc);
Assert.IsNotNull(doc.Items);
Assert.IsTrue(doc.Items.Count == 2);
Assert.IsTrue(doc.Items[0].Name == "X-requestId");
Assert.IsTrue(doc.Items[1].Name == "user");
EDIT:
I answered the question below. I can't believe I had to do that but it works.
Wow... So here is what I did to solve the problem. I'm not sure if there is an easier way and I can't believe json.net does not provide this out of the box.
var x = JObject.Parse(json);
var xx = x.Descendants().ToList().Where(d => d.Path.ToLower().EndsWith("$ref"));
foreach (var item in xx)
{
if (!item.HasValues)
continue;
string str = item.First.ToString().TrimStart(new char[] { '#' }).TrimStart(new char[] { '/' });
var split = str.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
JToken token = x;
for (int i = 0;i<split.Length; i++)
{
token = token[split[i]];
}
item.Parent.Replace(token);
}
var doc = JsonConvert.DeserializeObject<RootDoc>(x.ToString(), new JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
// Handle all errors
args.ErrorContext.Handled = true;
}
});
Assert.IsNotNull(doc);
Assert.IsNotNull(doc.Items);
Assert.IsTrue(doc.Items.Count == 2);
Assert.IsTrue(doc.Items[0].Name == "X-requestId");
Assert.IsTrue(doc.Items[1].Name == "user");
You can use this code.
JObject o = JObject.Parse(json);
JToken tokenToReplace = o.SelectToken("$..$ref");
string[] s = tokenToReplace.Value<string>().Split('/');
string a = s[s.Length - 1];
JToken newToken = o.SelectToken("$.." + a);
JArray items = (JArray)o["items"];
items.Add(newToken);
tokenToReplace.Parent.Parent.Remove();
string newJson = o.ToString();
newJson contains replaced $ref.
Related
I have json example "TestConfigIdMapping.json":
{
"schema": "None",
"TestMapping": [
{
"testId": "test_1",
"testConfigs": [
"platform1",
"platform2",
"platform3",
]
},
{
"testId": "test_2",
"testConfigs": [
"platform2"
]
}
]
}
Using Newtonsoft.Json c# library, how would I add json object element:
{
"testId": "test_3",
"testConfigs": [
"platform1",
"platform3"
]
}
to the above already loaded json file?
My code using form1 and display json in form2:
class JsonTestMapping
{
[JsonProperty("schema")]
string Schema = "None";
[JsonProperty("TestConfigMapping")]
public JsonObjects[] testConfigMapping { get; set; }
}
class JsonObjects
{
[JsonProperty("testId")]
public string TestId { get; set; }
[JsonProperty("testConfigs")]
public string[] TestConfigs { get; set; }
}
class LoadJson
{
public string jsonValues = File.ReadAllText(#"C:\GITTest\TestIdConfigMapping.json");
}
JsonObjects jsonObjects = new JsonObjects()
{
TestId = f1.Form1textbox.Text,
TestConfigs = new string[]
{
// set FormTextbox2 modifier = public
f1.FormTextbox2.Text
}
};
The following is Form2 popup and displays json:
private void Form2_Load(object sender, EventArgs e)
{
Form2TextBox.ReadOnly = true;
Form2TextBox.Multiline = true;
Form2TextBox.ScrollBars = ScrollBars.Vertical;
Form2TextBox.WordWrap = true;
//string convertJsonFileSerialized = JsonConvert.SerializeObject(readJsonFile.jsonText);
//List<ReadJsonFile> convertJsonFileDeserialized = JsonConvert.DeserializeObject<List<ReadJsonFile>>(readJsonFile.jsonText);
//string finalJson = JsonConvert.SerializeObject(convertJsonFileDeserialized);
LoadJson loadJson = new LoadJson();
string myJsonValues = loadJson.jsonValues;
JObject rss = JObject.Parse(myJsonValues);
JArray TestConfigMapping = (JArray)rss["TestConfigMapping"];
//List<JsonObjects> myList = JsonConvert.DeserializeObject<List<JsonObjects>>(myjsonRead);
JsonObjects jsonObjects = new JsonObjects()
{
TestId = f1.Form1textbox.Text,
TestConfigs = new string[]
{
// set FormTextbox2 modifier = public
f1.FormTextbox2.Text
}
};
List<JsonObjects> jsonList = new List<JsonObjects>();
jsonList.Add(jsonObjects);
string jsonResult1 = JsonConvert.SerializeObject(jsonObjects, Formatting.Indented);
JsonTestMapping jsonTestConfigMapping = new JsonTestMapping()
{
testConfigMapping = new JsonObjects[]
{
jsonObjects
}
};
string jsonResult = JsonConvert.SerializeObject(jsonTestConfigMapping, Formatting.Indented);
//ReadJsonFile readJsonFile = new ReadJsonFile();
//string jsonResult2 = readJsonFile.jsonText;
Form2TextBox.Text = rss.ToString();
}
The goal is to have Form 1, with 2 text box inputs that insert the "TestID" & "TestConfigs". Upon clicking button1, convert the 2 input texts into Json format and upon clicking "append" button, asserts the formatted Json object element into the already existing json file configuration.
I am having a string in the following format. I want to assign each JSON within the projects to a separate JObject. When I am trying to parse it and assign accordingly, I am unable to achieve it. How can I parse it?
{
"projects": [
{
"sno": "1",
"project_name": "Abs",
"project_Status": "Live"
},
{
"sno": "2",
"project_name": "Cgi",
"project_Status": "Live"
}
]
}
I have tried the following code;
using (StreamReader streamReader = new StreamReader(Path.Combine(Path.GetTempPath(), "sample.json")))
using (JsonTextReader reader = new JsonTextReader(streamReader))
{
var serializer = new JsonSerializer();
while (reader.Read())
{
if (reader.TokenType == JsonToken.StartObject)
{
JObject jsonPayload = JObject.Load(reader);
jsonProfile = jsonPayload.ToString();
JObject json = JObject.Parse(jsonProfile);
}
}
}
you can use DeserializeAnonymousType as a simpler solution to parse json.
var definition = new
{
projects = new[]{
new {
sno="1",
project_name= "Abs",
project_Status= "Live"
}
}
};
string json1 = #" {
'projects': [
{
'sno': '1',
'project_name': 'Abs',
'project_Status': 'Live'
},
{
'sno': '2',
'project_name': 'Cgi',
'project_Status': 'Live'
}
]
}";
var projects = JsonConvert.DeserializeAnonymousType(json1, definition);
but if you do want to use strong type, you'd better to use JsonProperty
public class Project
{
[JsonProperty("sno")]
public string Sno { get; set; }
[JsonProperty("project_name")]
public string ProjectName { get; set; }
[JsonProperty("project_Status")]
public string ProjectStatus { get; set; }
}
public class JsonModel
{
[JsonProperty("Projects")]
public List<Project> projects { get; set; }
}
JsonConvert.DeserializeObject<JsonModel>(json_Data);
I have a Json Object like the one below.
"log": {
"Response": [
{
"#type": "Authentication",
"Status": "True",
"Token": "cc622e9c-0d56-4774-8d79-543c525471b4"
},
{
"#type": "GetApplication",
"AppId": 100,
"Available": "True"
}]}
I need to access the appId property. I have tried the below code which gives the null reference error. Please help me figure out the mistake.
dynamic JsonText = JObject.Parse(result);
string AppId= JsonText ["log"]["Response #type='GetApplication'"]["AppId"].Tostring();
Here dotNetFiddle
string json = #"{
""log"": {
""Response"": [{
""#type"": ""Authentication"",
""Status"": ""True"",
""Token"": ""cc622e9c-0d56-4774-8d79-543c525471b4""
}, {
""#type"": ""GetApplication"",
""AppId"": 100,
""Available"": ""True""
}]
}
}";
JObject result = JObject.Parse(json);
foreach(var item in result["log"]["Response"])
{
Console.WriteLine(item["#type"]);
Console.WriteLine(item["AppId"]);
}
You don't need to use dynamic, use JObject and after that loop in the Responses and take the #type
To to access the AppId property like the way you are showing in your example:
string AppId = JObject.Parse(result)["log"].SelectToken("$.Response[?(#.#type=='GetApplication')]")["AppId"].ToString();
You can use http://json2csharp.com/ and generate model class, using Newtonsoft.Json and LINQ get id as I show.
model class
public class Response
{ [JsonProperty("#type")]
public string Type { get; set; }
[JsonProperty("Status")]
public string Status { get; set; }
[JsonProperty("Token")]
public string Token { get; set; }
[JsonProperty("AppId")]
public int? AppId { get; set; }
[JsonProperty("Available")]
public string Available { get; set; }
}
public class Log
{
public List<Response> Response { get; set; }
}
public class RootObject
{
public Log log { get; set; }
}
.cs
var results = JsonConvert.DeserializeObject<RootObject>(json);
var id= results.log.Response.FirstOrDefault(d => d.Type == "GetApplication").AppId;
string json = #"{
""log"": {
""Response"": [{
""#type"": ""Authentication"",
""Status"": ""True"",
""Token"": ""cc622e9c-0d56-4774-8d79-543c525471b4""
}, {
""#type"": ""GetApplication"",
""AppId"": 100,
""Available"": ""True""
}]
}
}";
JObject obj = JObject.Parse(result);
string AppId = obj["log"]["Response"][1]["AppId"].ToString();
Console.WriteLine(AppId);
#user3064309 hi, if ("#type": "GetApplication") is the second and not change location. so you can use obj["log"]["Response"][1]["AppId"].ToString();
I have written an extension function that will be the third level of depth. You can make its depth as generic if you need, so the code below
public static object GetJsonPropValue(this object obj, params string[] props)
{
try
{
var jsonString = obj.ToString().Replace("=", ":");
var objects = JsonConvert.DeserializeObject<dynamic>(jsonString);
foreach (var o in objects)
{
JObject jo = JObject.Parse("{" + o.ToString().Replace("=", ":") + "}");
if (props.Count() == 1 && jo.SelectToken(props[0]) != null)
return jo.SelectToken(props[0]).ToString();
if (props.Count() == 2 && jo.SelectToken(props[0])?.SelectToken(props[1]) != null)
return jo.SelectToken(props[0])?.SelectToken(props[1]).ToString();
if (props.Count() == 2 && jo.SelectToken(props[0])?.SelectToken(props[1])?.SelectToken(props[2]) != null)
return jo.SelectToken(props[0])?.SelectToken(props[1])?.SelectToken(props[2]).ToString();
}
}
catch (Exception ex)
{
throw new ArgumentException("GetJsonPropValue : " + ex.Message);
}
return null;
}
Also i written an extension for normal c# objects, getting prop value dynamically
public static object GetPropValue(this object obj, Type typeName, string propName)
{
try
{
IList<PropertyInfo> props = new List<PropertyInfo>(typeName.GetProperties());
foreach (PropertyInfo prop in props)
{
if (prop.Name == propName)
{
object propValue = prop.GetValue(obj, null);
return propValue;
}
}
}
catch (Exception ex)
{
throw new ArgumentException("GetPropValue : " + ex.Message);
}
return null;
}
I have a .json file that looks like this:
[
{
"username": "John",
"currency": 8,
"pulls":
[
{
"character": "person"
},
{
"character": "loved one"
}
]
},
{
"username": "Mike",
"currency": 2,
"pulls":
[
{
"character": "noone"
}
]
},
{
"username": "Clara",
"currency": 5,
"pulls":
[
{
"character": "someone"
}
]
}
]
What I managed to do so far is modify "currency":
bool userExists = false;
string jsonPointsString = File.ReadAllText(userPath);
dynamic jsonObjects = JsonConvert.DeserializeObject(jsonPointsString);
foreach (var jsonObject in jsonObjects)
{
if (jsonObject["username"] == user)
{
jsonObject["currency"] += value;
string output = JsonConvert.SerializeObject(jsonObjects, Formatting.Indented);
File.WriteAllText(userPath, output);
userExists = true;
}
}
As well as add a completely new entry from scratch:
JsonCollection.User user = new JsonCollection.User();
user.username = username;
user.currency = 10;
using (StreamReader r = new StreamReader(userPath))
{
string json = r.ReadToEnd();
List<JsonCollection.User> users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(json);
users.Add(user);
newJson = JsonConvert.SerializeObject(users, Formatting.Indented);
}
File.WriteAllText(userPath, newJson);
However, no matter what I try I can not add another element to "pulls". The idea is that I call a function with a username and a pull, two strings. Based on the username variable I have to find the corresponding Json Entry and create a new entry within the "pulls" tree based on the pull variable. This is what I could come up with:
public void AddPullToUser(string user, string newPull)
{
user = "Mike"; //test value
string jsonPointsString = File.ReadAllText(userPath);
dynamic jsonObjects = JsonConvert.DeserializeObject(jsonPointsString);
foreach (var jsonObject in jsonObjects)
{
if (jsonObject["username"] == user)
{
//jsonObject["pulls"] = newPull;
JsonCollection.Character pull = new JsonCollection.Character();
pull.character = newPull;
jsonObject["pulls"] = pull;
string output = JsonConvert.SerializeObject(jsonObjects, Formatting.Indented);
File.WriteAllText(userPath, output);
}
}
}
If I do it like this the system can't convert the JsonCollection to the JArray but without using the JArray I don't understand how to find the specific users tree.
In step two this will have to be expanded even further to not create duplicated "pulls", but first of all this has to work in general.
Any help would be greatly appreciated.
Something like this -
var json = "[{'username':'John','currency':8,'pulls':[{'character':'person'},{'character':'loved one'}]},{'username':'Mike','currency':2,'pulls':[{'character':'noone'}]},{'username':'Clara','currency':5,'pulls':[{'character':'someone'}]}]";
var obj = JsonConvert.DeserializeObject<List<RootObject>>(json);
var o = obj.FindIndex(a => a.username == "Mike");
obj[o].pulls.AddRange(new List<Pull>{
new Pull{
character = "Modified"
}
});
Console.WriteLine(JsonConvert.SerializeObject(obj));
Where
public class Pull
{
public string character { get; set; }
}
public class RootObject
{
public string username { get; set; }
public int currency { get; set; }
public List<Pull> pulls { get; set; }
}
alternatively, you might be interested in JSON Merge
A possible solution looks like -
var json = "[{'username':'John','currency':8,'pulls':[{'character':'person'},{'character':'loved one'}]},{'username':'Mike','currency':2,'pulls':[{'character':'noone'}]},{'username':'Clara','currency':5,'pulls':[{'character':'someone'}]}]";
var obj = JArray.Parse(json);
var idx = obj.IndexOf(obj.FirstOrDefault(a => a["username"].ToString() == "Mike"));
((JArray)obj[idx]["pulls"]).Add(JObject.Parse(#"{
'character': 'new one'
}"));
Console.WriteLine(obj[idx]);
/*output -
{
"username": "Mike",
"currency": 2,
"pulls": [
{
"character": "noone"
},
{
"character": "new one"
}
]
} */
After a bit more research and your help I was able to first of all change all the interaction with Json to the same code-style.
New entry has changed to this:
public void CreateUser(string username)
{
try
{
string jsonUserString = File.ReadAllText(userPath);
var users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(jsonUserString);
users.AddRange(new List<JsonCollection.User> { new JsonCollection.User { username = username, currency = 10, pulls = new List<JsonCollection.Character> { new JsonCollection.Character { character = "TemmieHYPE" } } } });
string output = JsonConvert.SerializeObject(users, Formatting.Indented);
File.WriteAllText(userPath, output);
}
catch
{
Console.WriteLine("Error on CreateUser");
}
}
Update has changed to this:
public void UpdateUserStats(string username, decimal value, int selection)
{
try
{
string jsonUserString = File.ReadAllText(userPath);
var users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(jsonUserString);
int user = users.FindIndex(a => (a.username == username));
if (user != -1)
{
switch (selection)
{
case 1:
users[user].currency += value;
break;
case 2:
users[user].secondsOnline += value;
break;
default:
break;
}
string output = JsonConvert.SerializeObject(users, Formatting.Indented);
File.WriteAllText(userPath, output);
//AddPullToUser(username, DateTime.Now.ToString()); //remove on live
}
else
{
CreateUser(username);
}
}
catch
{
Console.WriteLine("Error on UpdateCurrency");
}
}
Most importantly the Add Pull command to add a nested element to the Json now works with the following code:
public void AddPullToUser(string username, string pulledCharacter)
{
string jsonUserString = File.ReadAllText(userPath);
var users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(jsonUserString);
int alreadyPulled = users.FindIndex(a => (a.username == username) && (a.pulls.FindIndex(b => b.character == pulledCharacter) > 0));
if (alreadyPulled == -1)
{
int user = users.FindIndex(a => (a.username == username));
users[user].pulls.AddRange(new List<JsonCollection.Character> { new JsonCollection.Character { character = pulledCharacter } });
string output = JsonConvert.SerializeObject(users, Formatting.Indented);
File.WriteAllText(userPath, output);
}
}
With the addition of the "if (alreadyPulled == -1)" duplicated pulls don't get added to the Json files either.
I am trying to serialize some JSON for an API call:
string f5Name = "MyBigIpName";
string poolName = "myPoolName";
string postJson2 = JsonConvert.SerializeObject(
new
{
f5 = new {
f5Name = new {
poolName = memberState
},
}
}
);
This results in the following JSON:
{
"f5": {
"f5Name": {
"poolName": {
"member": {
"address": "10.0.0.0",
"port": 80
},
"session_state": "STATE_DISABLED"
}
}
}
}
However, what I am really looking to do is produce this JSON:
{
"f5": {
"MyBigIpName": {
"myPoolName": {
"member": {
"address": "10.0.0.0",
"port": 80
},
"session_state": "STATE_DISABLED"
}
}
}
}
Is there any way to have the f5Name and poolName property names be dynamic so that I can produce the above JSON? I am using Newtonsoft.JSON (JSON.NET)
I'm not sure if you could do something with a dynamic type or not, but you for sure can do something with Dictionaries:
var obj = new
{
f5 = new Dictionary<string, object>
{
{
f5Name, new Dictionary<string, object> {
{poolName, memberState}
}
}
}
}
string postJson2 = JsonConvert.SerializeObject(obj);
For what you are intending, you'd need to resolve your names in a contract resolver. Something like:
private class MyContractResolver : DefaultContractResolver
{
private Dictionary<string,string> _translate;
public MyContractResolver(Dictionary<string, string> translate)
{
_translate = translate;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
string newPropertyName;
if(_translate.TryGetValue(property.PropertyName, out newPropertyName))
property.PropertyName = newPropertyName;
return property;
}
}
And then:
string f5Name = "MyBigIpName";
string poolName = "MyPoolName";
var _translator = new Dictionary<string, string>()
{
{ "f5Name", f5Name },
{ "poolName", poolName },
};
string postJson2 = JsonConvert.SerializeObject(
new
{
f5 = new {
f5Name = new {
poolName = memberState
},
},
new JsonSerializerSettings { ContractResolver = new MyContractResolver(_translator) });
If you use C# 6, you could benefit from nameof and/or make it cleaner with a dictionary intializer:
var _translator = new Dictionary<string, string>()
{
[nameof(f5Name)] = f5Name ,
[nameof(poolName)] = poolName ,
};
The dictionary creation process could be automated easily too :-)