我有一个很大的任意JSON结构作为代码中的JObject
引用。
我想序列化此结构,除非遇到JObject
包含名为type
且值为"encrypted"
的属性,然后要删除相邻的data
属性编写对象。
换句话说,如果我遇到这个问题:
{
type: "encrypted",
name: "some-name",
data: "<base64-string>"
}
它将被序列化为:
{
type: "encrypted",
name: "some-name"
}
我无法更改结构,因此在更改之前克隆它效率太低,因此我尝试使用JsonConverter
如下:
public class RemoveEncryptedDataSerializer : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(JObject);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var o = (JObject)value;
if (o.Value<string>("type") != "encrypted")
{
o.WriteTo(writer);
return;
}
var copy = o.DeepClone();
copy["data"]?.Parent.Remove();
copy.WriteTo(writer);
}
}
然而,似乎仅使用不是从CanConvert
派生的类型来调用JToken
函数,因此从未调用我的WriteJson
函数。
还有另一种方法可以实现这一目标吗?
编辑:以下是一些可用于测试的代码:
[TestMethod]
public void ItShouldExcludeEncryptedData()
{
var input = JObject.Parse(@"
{
a: {
type: 'encrypted',
name: 'some-name',
data: 'some-data'
}
}");
var expected = JObject.Parse(@"
{
a: {
type: 'encrypted',
name: 'some-name',
}
}");
var output = input.ToString(Formatting.Indented, new RemoveEncryptedDataSerializer());
Assert.AreEqual(
expected.ToString(Formatting.Indented),
output);
}
答案 0 :(得分:2)
需要构建转换器来处理JToken
,并且该转换器必须递归工作以确保编辑所有加密数据。
我能够使以下转换器正常工作:
public class RemoveEncryptedDataConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(JToken).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken token = (JToken)value;
if (token.Type == JTokenType.Object)
{
bool omitDataProperty = token.Value<string>("type") == "encrypted";
writer.WriteStartObject();
foreach (var prop in token.Children<JProperty>())
{
if (omitDataProperty && prop.Name == "data")
continue;
writer.WritePropertyName(prop.Name);
serializer.Serialize(writer, prop.Value); // recurse
}
writer.WriteEndObject();
}
else if (token.Type == JTokenType.Array)
{
writer.WriteStartArray();
foreach (var item in token.Children())
{
serializer.Serialize(writer, item); // recurse
}
writer.WriteEndArray();
}
else // JValue
{
token.WriteTo(writer);
}
}
}
正在运行的演示:https://dotnetfiddle.net/0K61Bz
如果要直接通过流使用JsonWriter
,则可以将转换器中的逻辑重构为递归扩展方法,并使用该方法。如果不使用串行器,则不需要转换器。
public static class JsonExtensions
{
public static void RedactedWriteTo(this JToken token, JsonWriter writer)
{
if (token.Type == JTokenType.Object)
{
bool omitDataProperty = token.Value<string>("type") == "encrypted";
writer.WriteStartObject();
foreach (var prop in token.Children<JProperty>())
{
if (omitDataProperty && prop.Name == "data")
continue;
writer.WritePropertyName(prop.Name);
prop.Value.RedactedWriteTo(writer); // recurse
}
writer.WriteEndObject();
}
else if (token.Type == JTokenType.Array)
{
writer.WriteStartArray();
foreach (var item in token.Children())
{
item.RedactedWriteTo(writer); // recurse
}
writer.WriteEndArray();
}
else // JValue
{
token.WriteTo(writer);
}
}
}
然后您可以像这样使用它,其中stream
是您的输出流,input
是您的JObject
:
using (StreamWriter sw = new StreamWriter(stream)) // or StringWriter if you prefer
using (JsonWriter writer = new JsonTextWriter(sw))
{
writer.Formatting = Formatting.Indented;
input.RedactedWriteTo(writer);
}
答案 1 :(得分:0)
假设您正在使用Newtonsoft JSON.Net库;
要有条件地序列化属性,请添加一个方法,该方法返回与属性名称相同的布尔值,然后在方法名称前加上ShouldSerialize。方法的结果确定属性是否被序列化。如果该方法返回true,则该属性将被序列化;如果返回false,则该属性将被跳过。
例如:
public class EncryptedData
{
public string Type { get; set; }
public string Name { get; set; }
public string Data { get; set; }
public bool ShouldSerializeData()
{
// don't serialize the Data property if the Type equals "encrypted"
return (Type != "encrypted");
}
}