簡體   English   中英

在過濾掉某些屬性的同時序列化JSON.NET JObject

[英]Serializing a JSON.NET JObject while filtering out certain properties

我的代碼中有一個大的任意JSON結構作為JObject引用。

我想序列化這個結構,除非我遇到一個包含一個名為type的屬性的JObject ,其值為"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派生的類型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);
}

需要構建轉換器來處理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);
}

小提琴: https//dotnetfiddle.net/l949HU

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM