我有一些json
输入我无法预测的形状,我必须进行一些转换(称之为某些内容),以便不记录某些字段,例如,如果我有这个json
{
"id": 5,
"name": "Peter",
"password": "some pwd"
}
然后在转换后它看起来像这样:
{
"id": 5,
"name": "Peter"
}
样本很简单,实际情况不是那么开心/容易我会有一些正则表达式,如果输入json 上的任何字段匹配,那么就不会在结果上,如果我有一些嵌套对象,我将不得不递归。我一直在看 Linq到Json 的一些东西,但我发现没有什么能满足我的需求。
有没有办法做到这一点?
注意的:
这是日志记录库的一部分,如果需要或更容易,我可以使用json string
。问题是,在我的日志记录管道中的某个时刻,我得到对象(或根据需要的字符串)然后我需要从敏感数据中删除它,例如密码,还有任何其他客户端 - 指定数据。
答案 0 :(得分:4)
@dbc打败了我,但这是我的版本基本相同的想法。
您可以将JSON解析为JToken
,然后使用递归帮助方法将属性名称与正则表达式匹配。只要匹配,您可以从其父对象中删除该属性。删除所有敏感信息后,只需使用JToken.ToString()
获取编辑的JSON。
这是辅助方法的样子:
public static string RemoveSensitiveProperties(string json, IEnumerable<Regex> regexes)
{
JToken token = JToken.Parse(json);
RemoveSensitiveProperties(token, regexes);
return token.ToString();
}
public static void RemoveSensitiveProperties(JToken token, IEnumerable<Regex> regexes)
{
if (token.Type == JTokenType.Object)
{
foreach (JProperty prop in token.Children<JProperty>().ToList())
{
bool removed = false;
foreach (Regex regex in regexes)
{
if (regex.IsMatch(prop.Name))
{
prop.Remove();
removed = true;
break;
}
}
if (!removed)
{
RemoveSensitiveProperties(prop.Value, regexes);
}
}
}
else if (token.Type == JTokenType.Array)
{
foreach (JToken child in token.Children())
{
RemoveSensitiveProperties(child, regexes);
}
}
}
以下是其使用的简短演示:
public static void Test()
{
string json = @"
{
""users"": [
{
""id"": 5,
""name"": ""Peter Gibbons"",
""company"": ""Initech"",
""login"": ""pgibbons"",
""password"": ""Sup3rS3cr3tP@ssw0rd!"",
""financialDetails"": {
""creditCards"": [
{
""vendor"": ""Viza"",
""cardNumber"": ""1000200030004000"",
""expDate"": ""2017-10-18"",
""securityCode"": 123,
""lastUse"": ""2016-10-15""
},
{
""vendor"": ""MasterCharge"",
""cardNumber"": ""1001200230034004"",
""expDate"": ""2018-05-21"",
""securityCode"": 789,
""lastUse"": ""2016-10-02""
}
],
""bankAccounts"": [
{
""accountType"": ""checking"",
""accountNumber"": ""12345678901"",
""financialInsitution"": ""1st Bank of USA"",
""routingNumber"": ""012345670""
}
]
},
""securityAnswers"":
[
""Constantinople"",
""Goldfinkle"",
""Poppykosh"",
],
""interests"": ""Computer security, numbers and passwords""
}
]
}";
Regex[] regexes = new Regex[]
{
new Regex("^.*password.*$", RegexOptions.IgnoreCase),
new Regex("^.*number$", RegexOptions.IgnoreCase),
new Regex("^expDate$", RegexOptions.IgnoreCase),
new Regex("^security.*$", RegexOptions.IgnoreCase),
};
string redactedJson = RemoveSensitiveProperties(json, regexes);
Console.WriteLine(redactedJson);
}
以下是结果输出:
{
"users": [
{
"id": 5,
"name": "Peter Gibbons",
"company": "Initech",
"login": "pgibbons",
"financialDetails": {
"creditCards": [
{
"vendor": "Viza",
"lastUse": "2016-10-15"
},
{
"vendor": "MasterCharge",
"lastUse": "2016-10-02"
}
],
"bankAccounts": [
{
"accountType": "checking",
"financialInsitution": "1st Bank of USA"
}
]
},
"interests": "Computer security, numbers and passwords"
}
]
}
答案 1 :(得分:2)
您可以将JSON解析为JContainer
(对象或数组),然后使用DescendantsAndSelf()
搜索JSON层次结构,查找名称与某些Regex
匹配的属性,或者与Regex
匹配的字符串值,并使用JToken.Remove()
删除这些项。
例如,给定以下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"
]
}
您可以删除名称中包含"pass.*w.*r.*d"
的所有属性,如下所示:
var root = (JContainer)JToken.Parse(jsonString);
var nameRegex = new Regex(".*pass.*w.*r.*d.*", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
var query = root.DescendantsAndSelf()
.OfType<JProperty>()
.Where(p => nameRegex.IsMatch(p.Name));
query.RemoveFromLowestPossibleParent();
结果是:
{
"Items": [
{
"id": 5,
"name": "Peter"
},
{
"id": 5,
"name": "Peter"
}
],
"SecretData": "This data is secret",
"StringArray": [
"I am public",
"This is also secret"
]
}
您可以通过执行以下操作删除包含子字符串secret
的所有字符串值:
var valueRegex = new Regex(".*secret.*", RegexOptions.IgnoreCase);
var query2 = root.DescendantsAndSelf()
.OfType<JValue>()
.Where(v => v.Type == JTokenType.String && valueRegex.IsMatch((string)v));
query2.RemoveFromLowestPossibleParent();
var finalJsonString = root.ToString();
在第一次转换后应用时会导致:
{
"Items": [
{
"id": 5,
"name": "Peter"
},
{
"id": 5,
"name": "Peter"
}
],
"StringArray": [
"I am public"
]
}
为方便起见,我使用以下扩展方法:
public static class JsonExtensions
{
public static IEnumerable<TJToken> RemoveFromLowestPossibleParent<TJToken>(this IEnumerable<TJToken> nodes) where TJToken : JToken
{
if (nodes == null)
return null;
foreach (var node in nodes.ToList())
node.RemoveFromLowestPossibleParent();
return nodes;
}
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
}