Reputation: 1112
How do I search for the path of a JObject in my deserialized JSON by value using regex or other wildcard-capable method?
For instance, this is my JSON:
{
"type": "AdaptiveCard",
"body": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "{item.name}",
"size": "Large",
"weight": "Bolder",
"horizontalAlignment": "Center",
"color": "Accent"
},
{
"type": "Image",
"url": "{item.image}",
"altText": ""
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"horizontalAlignment": "Right",
"items": [
{
"type": "TextBlock",
"text": "{template.other}",
"weight": "Bolder",
"horizontalAlignment": "Right"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "{item.other}"
}
]
}
]
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"horizontalAlignment": "Right",
"items": [
{
"type": "TextBlock",
"text": "{template.name}",
"weight": "Bolder",
"horizontalAlignment": "Right"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "{item.name}"
}
]
}
]
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"horizontalAlignment": "Right",
"items": [
{
"type": "TextBlock",
"text": "{template.order}",
"weight": "Bolder",
"horizontalAlignment": "Right"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "{item.order}"
}
]
}
]
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"horizontalAlignment": "Right",
"items": [
{
"type": "TextBlock",
"text": "{template.family}",
"weight": "Bolder",
"horizontalAlignment": "Right"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "{item.genus}"
}
]
}
]
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"horizontalAlignment": "Right",
"items": [
{
"type": "TextBlock",
"text": "{template.species}",
"weight": "Bolder",
"horizontalAlignment": "Right"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "{item.species}"
}
]
}
]
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"horizontalAlignment": "Right",
"items": [
{
"type": "TextBlock",
"text": "{template.clade}",
"weight": "Bolder",
"horizontalAlignment": "Right"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "Container",
"items": [
{
"$data": "{item.clade}",
"type": "TextBlock",
"text": "{$data}"
}
]
}
]
}
]
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0"
}
How would I find the path of the element with a value that contains {item.name}
with JSON.NET?
I've looked at the SelectToken
method, but I can't figure out how to make it work for what I'm trying to do.
Upvotes: 1
Views: 1313
Reputation: 116991
If you are using LINQ to JSON and want to find the paths to all JValue
values containing the "{item.name}"
string (or more generally matching any regular expression), you have a couple options.
Let's say your regular expression looks like this:
var innerValue = Regex.Escape(@"{item.name}");
var regexString = $".*{innerValue}.*"; // Evaluates to .*\{item\.name}.*
And you have some JObject obj
capturing your JSON hierarchy.
Firstly, you could search for all string-valued tokens whose value matches this expression using JContainer.DescendantsAndSelf()
:
var regex = new Regex(regexString);
var paths = obj.Descendants().OfType<JValue>().Where(v => v.Type == JTokenType.String)
.Where(v => regex.IsMatch((string)v.Value))
.Select(v => v.Path)
.ToList();
Secondly, you could use SelectTokens()
with the JSONPath regex operator:
var jsonPath = $"..[?(@ =~ /{regexString}/)]"; // Note that the regex should be delimited by `/` characters.
var paths = obj.SelectTokens(jsonPath)
.Select(v => v.Path)
.ToList();
Notes:
There is no standard for JSONPath other than the short article # JSONPath - XPath for JSON.
..
is the JSONPath "recursive descent" operator.
@
represents the current object.
[?( )]
applies a filter (script) expression using the "underlying script engine".
=~
is Newtonsoft's regular expression operator. It was introduced in Json.NET 11.0.1.
Within the JSONPath string, the regular expression needs to be delimited by /
characters (rather than '
).
Given that there's no formal definition for JSONPath filter expressions I feel as though the first option is a little more straightforward.
Once you have a selected set of JValue
values, you can use JToken.Ancestors()
to climb up the parent hierarchy to find the lowest parent JObject
.
Demo fiddle here.
Upvotes: 4