Reputation: 2822
I'm relatively fluent using json.net, but I'm struggling to pull this value in a straightforward way.
I feel like json.net should be able to infer what I'm trying to do so that I don't have to cast every step along the way to get what I want.
Below is my first failed attempt and a second attempt which I got to work. While the second attempt does work, I'd like to simplify the code to be somewhat more like the first one.
Note that I can't assume that the "C" object is the one that contains the object I want.
var jt = JToken.Parse(@"{
""A"": {
""name"": ""object1"",
""order"": ""1"",
""type"": ""val""
},
""B"": {
""order"": ""2"",
""type"": ""val"",
},
""C"": {
""name"": ""object3"",
""type"": ""val"",
""answer"": ""Yes"", ""<------ comment"": ""this is the value I'm trying to get""
}
}");
//var firstAttempt = (jt.First(j => j["name"] == "object3"))["answer"]; // cannot be applied to operands of type 'JToken' and 'String'
var thisWorksFine = jt.First(a => a.ToObject<JProperty>()
.Value.ToObject<JObject>()["name"]?.ToString() == "object3")
.ToObject<JProperty>()
.Value["answer"];
So my question is, is there a way to get the value without having to cast each object/property and ultimately have code that looks more like my first attempt? If my second attempt is more or less what has to be done, that's fine.
Upvotes: 1
Views: 69
Reputation: 129777
You seem to be jumping through more hoops than you need to. I would rewrite your query like this:
var answer = jt.Children<JProperty>()
.Where(jp => (string)jp.Value["name"] == "object3")
.Select(jp => (string)jp.Value["answer"])
.FirstOrDefault();
Fiddle: https://dotnetfiddle.net/blKne6
The above can be simplified somewhat to the following, which is a little closer to your first attempt:
var answer = (string)jt.Children<JProperty>()
.FirstOrDefault(jp => (string)jp.Value["name"] == "object3")
?.Value["answer"];
Fiddle: https://dotnetfiddle.net/3Iw8Uu
Upvotes: 1
Reputation: 3293
Do you specifically want to always get the answer under the "C" object?
If so you can use:
var jToken = JToken.Parse("{\r\n\t\"A\": {\r\n\t\t\"name\": \"object1\",\r\n\t\t\"order\": \"1\",\r\n\t\t\"type\": \"val\"\r\n\t},\r\n\t\"B\": {\r\n\t\t\"name\": \"object2\",\r\n\t\t\"order\": \"2\",\r\n\t\t\"type\": \"val\"\r\n\t},\r\n\t\"C\": {\r\n\t\t\"name\": \"object3\",\r\n\t\t\"type\": \"val\",\r\n\t\t\"answer\": \"Yes\"\r\n\t}\r\n}");
var answer = jToken.SelectToken("C.answer");
Upvotes: 1
Reputation: 784
I've used QuickType to make a model of your Json code and then parsed it and did the filtering on the resulting objects which is much easier. Code:
public class MyElement
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("order")]
public long Order { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("answer")]
public string Answer { get; set; }
}
And parsing:
string json = @"{
""A"": {
""name"": ""object1"",
""order"": ""1"",
""type"": ""val""
},
""B"": {
""order"": ""2"",
""type"": ""val"",
},
""C"": {
""name"": ""object3"",
""type"": ""val"",
""answer"": ""Yes"",
}
}";
Dictionary<string, MyElement> elements = JsonConvert.DeserializeObject<Dictionary<string, MyElement>>(json);
string answerOfObject3 = elements.Values.FirstOrDefault(element => element.Name == "object3")?.Answer;
Upvotes: 1