[英]Serialize to JSON only some records from a dictionary in a complex type
I have the following contract:我有以下合同:
class Information
{
public string SensitiveInformation { get; set; }
public string NotSensitiveInformation { get; set; }
public IDictionary<string, string> PartialSensitiveInformation { get; set; }
}
My goal is to serialize the class, but I need to ommit some sensitive information.我的目标是序列化类,但我需要省略一些敏感信息。 for this I've created a contract resolver:
为此,我创建了一个合同解析器:
class IgnorePropertiesContractResolver : DefaultContractResolver
{
private readonly HashSet<string> propertyNamesToIgnore;
public IgnorePropertiesContractResolver(HashSet<string> propertyNamesToIgnore)
{
this.propertyNamesToIgnore = propertyNamesToIgnore;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty jsonProperty = base.CreateProperty(member, memberSerialization);
if (this.propertyNamesToIgnore.Contains(jsonProperty.PropertyName))
{
jsonProperty.ShouldSerialize = x => false;
}
return jsonProperty;
}
}
and running the code:并运行代码:
IgnorePropertiesContractResolver resolver = new IgnorePropertiesContractResolver(new HashSet<string> {"SensitiveInformation" });
Information info = new Information();
info.SensitiveInformation = "sensitive data";
info.NotSensitiveInformation = "not sensitive data";
info.PartialSensitiveInformation = new Dictionary<string, string>();
info.PartialSensitiveInformation["secret_data"] = "secret data";
info.PartialSensitiveInformation["not_secret_data"] = "not secret data";
var data = JsonConvert.SerializeObject(info, new JsonSerializerSettings { ContractResolver = resolver });
Returns this data: {"NotSensitiveInformation":"not sensitive data","PartialSensitiveInformation":{ "secret_data":"secret data" ,"not_secret_data":"not secret data"}}返回此数据: {"NotSensitiveInformation":"非敏感数据","PartialSensitiveInformation":{ "secret_data":"secret data" ,"not_secret_data":"not secret data"}}
Hor to change my contract resolver so I can ommit fom the serialization certain keys from the dictionary PartialSensitiveInformation ? Hor 更改我的合同解析器,以便我可以从字典PartialSensitiveInformation 中省略序列化某些键? I don't want to serialize the key "secret_data".
我不想序列化密钥“secret_data”。
Please note that I have the contract in a nuget so adding attribute is not possible in this case.请注意,我在 nuget 中有合同,因此在这种情况下无法添加属性。
I'm using .net franework 4.7.2.我正在使用 .net franework 4.7.2。
You can implement a custom value provider (using IValueProvider
) to handle this.您可以实现自定义值提供程序(使用
IValueProvider
)来处理此问题。 The value provider can take in the list of sensitive property names, and transform the dictionary to exclude the sensitive properties:值提供者可以接受敏感属性名称列表,并转换字典以排除敏感属性:
public class PartialSensitiveInformationValueProvider : IValueProvider
{
private readonly HashSet<string> _propertyNamesToIgnore;
public PartialSensitiveInformationValueProvider(HashSet<string> propertyNamesToIgnore)
{
_propertyNamesToIgnore = propertyNamesToIgnore;
}
public object GetValue(object target)
{
return ((Information)target).PartialSensitiveInformation
.Where(x => !_propertyNamesToIgnore.Contains(x.Key))
.ToDictionary(k => k.Key, v => v.Value);
}
public void SetValue(object target, object value)
{
throw new NotImplementedException();
}
}
You can then use this value provider in the contract resolver:然后,您可以在合同解析器中使用此值提供程序:
public class IgnorePropertiesContractResolver : DefaultContractResolver
{
private readonly HashSet<string> propertyNamesToIgnore;
public IgnorePropertiesContractResolver(HashSet<string> propertyNamesToIgnore)
{
this.propertyNamesToIgnore = propertyNamesToIgnore;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty jsonProperty = base.CreateProperty(member, memberSerialization);
if (this.propertyNamesToIgnore.Contains(jsonProperty.PropertyName))
{
jsonProperty.ShouldSerialize = x => false;
}
if (jsonProperty.PropertyName == nameof(Information.PartialSensitiveInformation))
{
jsonProperty.ValueProvider = new PartialSensitiveInformationValueProvider(this.propertyNamesToIgnore);
}
return jsonProperty;
}
}
Using the example you have (including "secret_data" in the propertyNamesToIgnore
), the results are now:使用(包括在“secret_data”你有例子
propertyNamesToIgnore
),现在的结果:
{"NotSensitiveInformation":"not sensitive data","PartialSensitiveInformation":{"not_secret_data":"not secret data"}}
My answer is a bit more involved than @devNull 's, the difference being it's a generic approach:我的回答比@devNull的回答要复杂一些,不同之处在于它是一种通用方法:
It allows you to target specific members using an expression, eg s => s.Inner.Dictionary
.它允许您使用表达式来定位特定成员,例如
s => s.Inner.Dictionary
。
Sample data:样本数据:
public class MyData
{
public Dictionary<string, string> Dictionary { get; set; } = new Dictionary<string, string>();
public MyDataInner Inner { get; set; } = new MyDataInner();
}
public class MyDataInner
{
public Dictionary<string, string> Dictionary { get; set; } = new Dictionary<string, string>();
}
Newtonsoft related classes, contract, converter, resolver: Newtonsoft 相关类、合约、转换器、解析器:
public class MyJsonDictionaryContract : JsonDictionaryContract
{
public MyJsonDictionaryContract(Type underlyingType, (string, string[])[] filters) : base(underlyingType)
{
Converter = new MyJsonConverter(filters);
}
}
internal class MyJsonConverter : JsonConverter<IDictionary<string, string>>
{
public MyJsonConverter((string, string[])[] filters)
{
Filters = filters;
}
private (string, string[])[] Filters { get; }
public override void WriteJson(JsonWriter writer, IDictionary<string, string> value, JsonSerializer serializer)
{
foreach (var (k, v) in value)
{
var tuple = Filters.FirstOrDefault(s => s.Item1 == writer.Path);
if (tuple != default && tuple.Item2.Contains(k))
continue; // if Filters has path and key then NOP
writer.WriteStartObject();
writer.WritePropertyName(k);
writer.WriteValue(v);
writer.WriteEndObject();
}
}
public override IDictionary<string, string> ReadJson(
JsonReader reader,
Type objectType,
IDictionary<string, string> existingValue,
bool hasExistingValue,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class MyDataContractResolver : DefaultContractResolver
{
public MyDataContractResolver((string, string[])[] filters)
{
Filters = filters;
}
private (string, string[])[] Filters { get; }
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
{
return new MyJsonDictionaryContract(objectType, Filters);
}
}
The example:这个例子:
internal static class Program
{
private static void Main(string[] args)
{
var data = new MyData
{
Dictionary = new Dictionary<string, string>
{
{"public", "abcd"},
{"private", "abcd"}
},
Inner = new MyDataInner
{
Dictionary = new Dictionary<string, string>
{
{"public", "abcd"},
{"private", "abcd"}
}
}
};
var filters = new[]
{
(ExpressionUtils.GetPath(data, s => s.Dictionary), new[] {"private"}),
(ExpressionUtils.GetPath(data, s => s.Inner.Dictionary), new[] {"public"})
};
var resolver = new MyDataContractResolver(filters);
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = resolver
};
var json = JsonConvert.SerializeObject(data, settings);
}
}
internal static class ExpressionUtils
{
/// <summary>
/// expression 2 path, e.g. class.prop1.prop2
/// </summary>
public static string GetPath<TSource, TProperty>(TSource source, Expression<Func<TSource, TProperty>> property)
{
// https://stackoverflow.com/a/1667533/361899
var expression = property.Body.NodeType switch
{
ExpressionType.Convert => (property.Body is UnaryExpression ue ? ue.Operand : null) as MemberExpression,
ExpressionType.ConvertChecked =>
(property.Body is UnaryExpression ue ? ue.Operand : null) as MemberExpression,
_ => property.Body as MemberExpression
};
var stack = new Stack<string>();
while (expression != null)
{
stack.Push(expression.Member.Name);
expression = expression.Expression as MemberExpression;
}
var path = string.Join(".", stack);
return path;
}
}
Result without it:没有它的结果:
{
"Dictionary": {
"public": "abcd",
"private": "abcd"
},
"Inner": {
"Dictionary": {
"public": "abcd",
"private": "abcd"
}
}
}
Result with it:结果与它:
{
"Dictionary": {
"public": "abcd"
},
"Inner": {
"Dictionary": {
"private": "abcd"
}
}
}
So there you are, you can filter by key, but target any member whether it's nested or not.所以你是这样的,你可以按键过滤,但无论是否嵌套,都可以定位任何成员。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.