I have some JSON input, the shape of which I cannot predict, and I have to make some transformations (to call it something) so that some fields are not logged. For instance,
You can parse your JSON to a JContainer (which is either an object or array), then search the JSON hierarchy using DescendantsAndSelf() for properties with names that match some Regex, or string values that match a Regex, and remove those items with JToken.Remove().
For instance, given the following JSON:
{
"Items": [
{
"id": 5,
"name": "Peter",
"password": "some pwd"
},
{
"id": 5,
"name": "Peter",
"password": "some pwd"
}
],
"RootPasswrd2": "some pwd",
"SecretData": "This data is secret",
"StringArray": [
"I am public",
"This is also secret"
]
}
You can remove all properties whose name includes "pass.*w.*r.*d" as follows:
var root = (JContainer)JToken.Parse(jsonString);
var nameRegex = new Regex(".*pass.*w.*r.*d.*", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
var query = root.DescendantsAndSelf()
.OfType()
.Where(p => nameRegex.IsMatch(p.Name));
query.RemoveFromLowestPossibleParents();
Which results in:
{
"Items": [
{
"id": 5,
"name": "Peter"
},
{
"id": 5,
"name": "Peter"
}
],
"SecretData": "This data is secret",
"StringArray": [
"I am public",
"This is also secret"
]
}
And you can remove all string values that include the substring secret by doing:
var valueRegex = new Regex(".*secret.*", RegexOptions.IgnoreCase);
var query2 = root.DescendantsAndSelf()
.OfType()
.Where(v => v.Type == JTokenType.String && valueRegex.IsMatch((string)v));
query2.RemoveFromLowestPossibleParents();
var finalJsonString = root.ToString();
Which when applied after the first transform results in:
{
"Items": [
{
"id": 5,
"name": "Peter"
},
{
"id": 5,
"name": "Peter"
}
],
"StringArray": [
"I am public"
]
}
For convenience, I am using the following extension methods:
public static partial class JsonExtensions
{
public static TJToken RemoveFromLowestPossibleParent(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
JToken toRemove;
var property = node.Parent as JProperty;
if (property != null)
{
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
toRemove = property;
property.Value = null;
}
else
{
toRemove = node;
}
if (toRemove.Parent != null)
toRemove.Remove();
return node;
}
public static IEnumerable RemoveFromLowestPossibleParents(this IEnumerable nodes) where TJToken : JToken
{
var list = nodes.ToList();
foreach (var node in list)
node.RemoveFromLowestPossibleParent();
return list;
}
}
Demo fiddle here.