Searching for a Specific Jtoken by Name in a Jobject Hierarchy

Searching for a specific JToken by name in a JObject hierarchy

If you are looking for a very specific token and know the path to it, you can navigate to it easily using the built-in SelectToken() method. For example:

string distance = jObject.SelectToken("routes[0].legs[0].distance.text").ToString();

If you need to find all occurences of a token with a given name in your JSON, no matter where they occur, then yes you'd need a recursive method. Here is one that might do the trick:

public static class JsonExtensions
{
public static List<JToken> FindTokens(this JToken containerToken, string name)
{
List<JToken> matches = new List<JToken>();
FindTokens(containerToken, name, matches);
return matches;
}

private static void FindTokens(JToken containerToken, string name, List<JToken> matches)
{
if (containerToken.Type == JTokenType.Object)
{
foreach (JProperty child in containerToken.Children<JProperty>())
{
if (child.Name == name)
{
matches.Add(child.Value);
}
FindTokens(child.Value, name, matches);
}
}
else if (containerToken.Type == JTokenType.Array)
{
foreach (JToken child in containerToken.Children())
{
FindTokens(child, name, matches);
}
}
}
}

Here is a demo:

class Program
{
static void Main(string[] args)
{
string json = @"
{
""routes"": [
{
""bounds"": {
""northeast"": {
""lat"": 50.4639653,
""lng"": 30.6325177
},
""southwest"": {
""lat"": 50.4599625,
""lng"": 30.6272425
}
},
""legs"": [
{
""distance"": {
""text"": ""1.7 km"",
""value"": 1729
},
""duration"": {
""text"": ""4 mins"",
""value"": 223
}
},
{
""distance"": {
""text"": ""2.3 km"",
""value"": 2301
},
""duration"": {
""text"": ""5 mins"",
""value"": 305
}
}
]
}
]
}";

JObject jo = JObject.Parse(json);

foreach (JToken token in jo.FindTokens("text"))
{
Console.WriteLine(token.Path + ": " + token.ToString());
}
}
}

Here is the output:

routes[0].legs[0].distance.text: 1.7 km
routes[0].legs[0].duration.text: 4 mins
routes[0].legs[1].distance.text: 2.3 km
routes[0].legs[1].duration.text: 5 mins

Searching for specific JToken in JObject hierarchy and store them in another JObject

Based on the sample JSON you've shown in your question, you should be able to get the result you want like this:

JObject obj = JObject.Parse(json);

JArray result = new JArray(
obj.SelectToken("memberdetails[0].properties")
.Select(jt => new JObject(new JProperty((string)jt["alias"],jt["value"]))));

Fiddle: https://dotnetfiddle.net/DOfKaJ

How can I call the value from a Jtoken

You can use SelectToken wherein you can select your desire value base on their path. See this reference.

JObject json = JObject.Parse(content);    
var value = json.SelectToken("role").Value<string>();

Add a path to select role. The path will depend on your JSON structure.

Example value of content

{
response : {
role : volunteer,
success: true,
token: eyJhbGci...
}
}

JObject json = JObject.Parse(content);
var value = json.SelectToken("response.role").Value<string>();

Read json value using C# without using Property

You could use

var jo = JObject.Parse(json);
var testCaseName = (string)jo.SelectToken($"$.value[?(@id=={idToSearch})].testCase.name");

Update

Base on your question in comment, you could find the ID with test case name using

jo.SelectTokens($"$.value[?(@testCase.name=='{nameToSearch}')].id")

Please note in this case, you would need to use .SelectTokens as there is chances of duplicates as give in the example in OP. You could get all the results or the first based on your requirement.

var idList  = jo.SelectTokens($"$.value[?(@testCase.name=='{nameToSearch}')].id")
.Select(x=> long.Parse(x.ToString()));
var onlyFirst = (long)jo.SelectTokens($"$.value[?(@testCase.name=='{nameToSearch}')].id")
.First();

Iterate over a json input and create the treeview like hierarchy based on the keys taking account of the children keys

What you want to do is to map your deeply nested JSON into a c# tree where each node has two properties -- a string key and a long T_id -- as well as a collection of children of the same type.

You could model this as follows, using a list:

public partial class KeyIdObject
{
public string key { get; set; }
public long T_id { get; set; }
public List<KeyIdObject> Children { get; set; }
}

Once you have you data model, you need to use a recursive algorithm to generate your nodes. A related algorithms is shown in Searching for a specific JToken by name in a JObject hierarchy, but you need a two-stage recursion:

  • Descend through the JToken hierarchy until you find a JObject with a T_id property.

  • Once you have found a match, construct a KeyIdObject for it and populate its list of children by searching the matching JObject's children using a nested recursive search.

  • Then move on to the matches's next sibling in the outer recursive search.

This can be accomplished by introducing an extension method that searches for the topmost descendants of a given JToken that match a given condition:

public static partial class JsonExtensions
{
/// <summary>
/// Enumerates through all descendants of the given element, returning the topmost elements that match the given predicate
/// </summary>
/// <param name="root"></param>
/// <param name="filter"></param>
/// <returns></returns>
public static IEnumerable<TJToken> TopDescendantsWhere<TJToken>(this JToken root, Func<TJToken, bool> predicate) where TJToken : JToken
{
if (predicate == null)
throw new ArgumentNullException();
return GetTopDescendantsWhere<TJToken>(root, predicate, false);
}

static IEnumerable<TJToken> GetTopDescendantsWhere<TJToken>(JToken root, Func<TJToken, bool> predicate, bool includeSelf) where TJToken : JToken
{
if (root == null)
yield break;
if (includeSelf)
{
var currentOfType = root as TJToken;
if (currentOfType != null && predicate(currentOfType))
{
yield return currentOfType;
yield break;
}
}
var rootContainer = root as JContainer;
if (rootContainer == null)
yield break;
var current = root.First;
while (current != null)
{
var currentOfType = current as TJToken;
var isMatch = currentOfType != null && predicate(currentOfType);
if (isMatch)
yield return currentOfType;

// If a match, skip children, but if not, advance to the first child of the current element.
var next = (isMatch ? null : current.FirstChild());

if (next == null)
// If no first child, get the next sibling of the current element.
next = current.Next;

// If no more siblings, crawl up the list of parents until hitting the root, getting the next sibling of the lowest parent that has more siblings.
if (next == null)
{
for (var parent = current.Parent; parent != null && parent != root && next == null; parent = parent.Parent)
{
next = parent.Next;
}
}

current = next;
}
}

static JToken FirstChild(this JToken token)
{
var container = token as JContainer;
return container == null ? null : container.First;
}
}

Then, you can use it to generate a recursive List<KeyIdObject> like so:

public partial class KeyIdObject
{
public static List<KeyIdObject> ToIdObjects(JToken root)
{
return root.TopDescendantsWhere<JObject>(o => o["T_id"] != null)
.Select(o => new KeyIdObject { key = ((JProperty)o.Parent).Name, T_id = (long)o["T_id"], Children = ToIdObjects(o) })
.ToList();
}
}

Demo fiddle #1 here, which generates the following structure:

[
{
"key": "Soccer",
"T_id": 0,
"Children": [
{
"key": "ClubA",
"T_id": 1
},
{
"key": "ClubB",
"T_id": 2
},
{
"key": "SubA",
"T_id": 3,
"Children": [
{
"key": "SubE",
"T_id": 3
}
]
},
{
"key": "SubK",
"T_id": 3
}
]
}
]

However, in your JSON some of your object node(s), specifically "Clubs" and "Subs", do not have a T_id property. Thus, they can't be captured into the node hierarchy as there is no way to populate the long T_id value. If you do need to capture these nodes, you can modify your data model to have a nullable value for the id and capture the intermediate nodes as follows:

public partial class KeyIdObject
{
public string key { get; set; }
public long? T_id { get; set; }
public List<KeyIdObject> Children { get; set; }
}

public partial class KeyIdObject
{
public static List<KeyIdObject> ToIdObjects(JToken root)
{
return root.TopDescendantsWhere<JObject>(o => true)
.Select(o => new KeyIdObject { key = ((JProperty)o.Parent).Name, T_id = (long?)o["T_id"], Children = ToIdObjects(o) })
.ToList();
}
}

Demo fiddle #2 here.

Finally, if you are sure your keys are unique at any given level, you could use a dictionary instead of a list, like so:

public partial class IdObject
{
public long T_id { get; set; }
public Dictionary<string, IdObject> Children { get; set; }
}

public partial class IdObject
{
public static Dictionary<string, IdObject> ToIdObjects(JToken root)
{
return root.TopDescendantsWhere<JObject>(o => o["T_id"] != null)
.ToDictionary(o => ((JProperty)o.Parent).Name,
o => new IdObject { T_id = (long)o["T_id"], Children = ToIdObjects(o) });
}
}

Demo fiddle #3 here.

Note that, in all cases, I chose long instead of int for the T_id for safety.


Update

If you are going to bind this into a WPF TreeView or something similar like a Syncfusion.Xamarin.SfTreeView, you will want to implement INotifyPropertyChanged and use ObservableCollection<T>. You may also want to use a different ItemTemplate for nodes with and without T_id values, in which case you can define a different c# POCO for each case. The following is one example:

public abstract partial class KeyItemBase : INotifyPropertyChanged
{
public KeyItemBase() : this(null, Enumerable.Empty<KeyItemBase>()) { }

public KeyItemBase(string key, IEnumerable<KeyItemBase> children)
{
this.m_key = key;
this.m_children = new ObservableCollection<KeyItemBase>(children);
}

string m_key;
public string key
{
get { return m_key; }
set
{
m_key = value;
RaisedOnPropertyChanged("key");
}
}

ObservableCollection<KeyItemBase> m_children;
public ObservableCollection<KeyItemBase> Children { get { return m_children; } }

public event PropertyChangedEventHandler PropertyChanged;

protected void RaisedOnPropertyChanged(string _PropertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
changed(this, new PropertyChangedEventArgs(_PropertyName));
}
}
}

public abstract partial class KeyItemBase
{
// Generate clean JSON on re-serialization.
public bool ShouldSerializeChildren() { return Children != null && Children.Count > 0; }
}

public sealed class KeyItem : KeyItemBase
{
// Use for a JSON object with no T_id property.
// Bind an appropriate SfTreeView.ItemTemplate to this type.

public KeyItem() : base() { }

public KeyItem(string key, IEnumerable<KeyItemBase> children) : base(key, children) { }
}

public class KeyIdItem : KeyItemBase
{
// Use for a JSON object with a T_id property.
// Bind an appropriate SfTreeView.ItemTemplate to this type.

public KeyIdItem() : base() { }

public KeyIdItem(string key, IEnumerable<KeyItemBase> children, long t_id) : base(key, children) { this.m_id = t_id; }

long m_id;
public long T_id
{
get { return m_id; }
set
{
m_id = value;
RaisedOnPropertyChanged("T_id");
}
}
}

public static class KeyItemFactory
{
public static KeyItemBase ToKeyObject(string name, long? id, IEnumerable<KeyItemBase> children)
{
if (id == null)
return new KeyItem(name, children);
else
return new KeyIdItem(name, children, id.Value);
}

public static IEnumerable<KeyItemBase> ToKeyObjects(JToken root)
{
return root.TopDescendantsWhere<JObject>(o => true)
.Select(o => ToKeyObject(((JProperty)o.Parent).Name, (long?)o["T_id"], ToKeyObjects(o)));
}
}

Which you would use as follows:

var items = new ObservableCollection<KeyItemBase>(KeyItemFactory.ToKeyObjects(root));

// Now bind items to your ItemsSource
// https://help.syncfusion.com/cr/cref_files/xamarin/Syncfusion.SfTreeView.XForms~Syncfusion.XForms.TreeView.SfTreeView~ItemsSource.html

Demo fiddle #4 here.



Related Topics



Leave a reply



Submit