C# 序列化JSON.NET作业对象,同时过滤掉某些属性

C# 序列化JSON.NET作业对象,同时过滤掉某些属性,c#,.net,json.net,C#,.net,Json.net,我的代码中有一个大的任意JSON结构作为JObject引用 我想序列化此结构,除非遇到一个JObject包含名为type且值为“加密的”的属性,然后我想在写入对象之前删除相邻的数据属性 换句话说,如果我遇到这种情况: { type: "encrypted", name: "some-name", data: "<base64-string>" } 我无法对结构进行变异,在变异之前克隆它效率太低,因此我尝试使用JsonConverter,如下所示: public clas

我的代码中有一个大的任意JSON结构作为
JObject
引用

我想序列化此结构,除非遇到一个
JObject
包含名为
type
且值
为“加密的”
的属性,然后我想在写入对象之前删除相邻的
数据
属性

换句话说,如果我遇到这种情况:

{
  type: "encrypted",
  name: "some-name",
  data: "<base64-string>"
}
我无法对结构进行变异,在变异之前克隆它效率太低,因此我尝试使用
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);
    }
}

假设您使用的是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");
    }
}

需要构建转换器来处理
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);
        }
    }
}
然后您可以这样使用它,其中
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);
}

Fiddle:

尝试
返回true
而不是
返回objectType==typeof(JObject)
在你的
CanConvert
方法中,告诉我它是否有效。同时尝试将此方法添加到你的转换器=>
public override bool CanRead{get{return false;}}
谢谢@er shoaib,不幸的是,这不起作用,因为
WriteJson
仍然只为结构的左节点调用。谢谢,这是一个有用的技巧,但是在这种情况下,JSON是任意结构,因此我无法将其反序列化为具体类型。谢谢Brian,这真的很有帮助!有趣的是,我的测试仍然失败,直到我从使用
改为使用
JsonConvert.SerializeObject(…)
。实际上,我需要写入流,但是
WriteTo(…)
也会以与
ToString(…)
相同的方式失败。我有点惊讶他们产生了不同的输出,但这复制了它:。你知道我如何在得到与
JsonConvert.SerializeObject
相同的输出的同时写入流吗?@jamesthrley更新了我的答案以显示流的方法。
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);
        }
    }
}
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);
        }
    }
}
using (StreamWriter sw = new StreamWriter(stream))  // or StringWriter if you prefer
using (JsonWriter writer = new JsonTextWriter(sw))
{
    writer.Formatting = Formatting.Indented;
    input.RedactedWriteTo(writer);
}