为了账号安全,请及时绑定邮箱和手机立即绑定

取消转义后如何转义嵌入的 JSON

取消转义后如何转义嵌入的 JSON

C#
天涯尽头无女友 2021-11-28 16:38:23
使用 Json.NET 进行序列化时,我需要在反序列化时之前未转义后转义嵌入的 JSON。这意味着我根据这篇文章对 JSON 进行了转义。这是我的 JSON:{     "Message":null,   "Error":false,   "VData":{        "RNumber":null,      "BRNumber":"Session1"   },   "onlineFields":{        "CCode":"Web",      "MNumber":"15478655",      "Product":"100",      "JsonFile":"      {           \"evaluation\":{              \"number\":[                 {                    \"@paraID\":\"1000\",                  \"@Value\":\"\",                  \"@label\":\"We are america\"               },               {                    \"@paraID\":\"2000\",                  \"@Value\":\"100\",                  \"@label\":\"We are japan\"               },               {                    \"@paraID\":\"3000\",                  \"@Value\":\"1000\",                  \"@label\":\"We are UK\"               },               {                    \"@paraID\":\"4000\",                  \"@Value\":\"\",                  \"@label\":\"We are China\"               }            ]         }      } "        }}转义后,我将上述 JSON 绑定到我的模型类。它工作正常。将 JSON 绑定到我使用以下代码的模型。private static void showJSON(string testJson){    Response response = JsonConvert.DeserializeObject<Response>(testJson);    var dropdowns = response.OnlineFields.JsonFile;    string json = JsonConvert.SerializeObject(dropdowns, Newtonsoft.Json.Formatting.Indented);    Console.WriteLine(json);}将 JSON 绑定到模型后,有一些逻辑可以将值设置为 JSON 并返回未转义的 JSON。这意味着它也返回 unescaped JsonFile,我再次需要上述 JSON 格式(转义嵌入JsonFile)发送到客户端 API。这是未转义的 JSON 格式,我需要将其转换为上面的转义 JSON(转义嵌入JsonFile){     "Message":null,   "Error":false,   "VData":{        "RNumber":null,      "BRNumber":"Session1"   },之前我问过一个问题,如何将这种嵌入的JSON直接反序列化为c#类,但是那里的答案并没有解释如何以相同的格式重新序列化。我需要将上一个问题的答案扩展到写作。
查看完整描述

1 回答

?
胡说叔叔

TA贡献1804条经验 获得超8个赞

您可以EmbeddedLiteralConverter<T>从此答案扩展到如何在 JSON 对象中转换转义的 JSON 字符串?通过覆盖JsonConverter.WriteJson()并执行嵌套序列化,然后编写结果字符串文字,如下所示:


public class EmbeddedLiteralConverter<T> : JsonConverter

{

    public override bool CanConvert(Type objectType)

    {

        return typeof(T).IsAssignableFrom(objectType);

    }


    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)

    {

        using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite))

        {

            using (var sw = new StringWriter(writer.Culture))

            {

                // Copy relevant settings

                using (var nestedWriter = new JsonTextWriter(sw) 

                { 

                    DateFormatHandling = writer.DateFormatHandling,

                    DateFormatString = writer.DateFormatString,

                    DateTimeZoneHandling = writer.DateTimeZoneHandling,

                    StringEscapeHandling = writer.StringEscapeHandling,

                    FloatFormatHandling = writer.FloatFormatHandling,

                    Culture = writer.Culture,

                    // Remove if you don't want the escaped \r\n characters in the embedded JSON literal:

                    Formatting = writer.Formatting, 

                })

                {

                    serializer.Serialize(nestedWriter, value);

                }

                writer.WriteValue(sw.ToString());

            }

        }

    }


    [ThreadStatic]

    static bool disabled;


    // Disables the converter in a thread-safe manner.

    bool Disabled { get { return disabled; } set { disabled = value; } }


    public override bool CanWrite { get { return !Disabled; } }


    public override bool CanRead { get { return !Disabled; } }

    

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

    {

        if (reader.TokenType == JsonToken.Null)

            return null;

        var contract = serializer.ContractResolver.ResolveContract(objectType);

        if (contract is JsonPrimitiveContract)

            throw new JsonSerializationException("Invalid type: " + objectType);

        if (existingValue == null)

            existingValue = contract.DefaultCreator();

        if (reader.TokenType == JsonToken.String)

        {

            var json = (string)JToken.Load(reader);

            using (var subReader = new JsonTextReader(new StringReader(json)))

            {

                // By populating a pre-allocated instance we avoid an infinite recursion in EmbeddedLiteralConverter<T>.ReadJson()

                // Re-use the existing serializer to preserve settings.

                serializer.Populate(subReader, existingValue);

            }

        }

        else

        {

            serializer.Populate(reader, existingValue);

        }

        return existingValue;

    }

}


struct PushValue<T> : IDisposable

{

    Action<T> setValue;

    T oldValue;


    public PushValue(T value, Func<T> getValue, Action<T> setValue)

    {

        if (getValue == null || setValue == null)

            throw new ArgumentNullException();

        this.setValue = setValue;

        this.oldValue = getValue();

        setValue(value);

    }


    #region IDisposable Members


    // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.

    public void Dispose()

    {

        if (setValue != null)

            setValue(oldValue);

    }


    #endregion

}

然后,JsonSerializerSettings.Converters在反序列化和序列化时添加转换器:


var settings = new JsonSerializerSettings

{

    Converters = { new EmbeddedLiteralConverter<JsonFile>() },

};


var response = JsonConvert.DeserializeObject<Response>(testJson, settings);


var json2 = JsonConvert.SerializeObject(response, Formatting.Indented, settings);

或者,您可以使用如下方式将转换器直接应用于您的模型JsonConverterAttribute:


public class OnlineFields

{

    public string CCode { get; set; }

    public string MNumber { get; set; }

    public string Product { get; set; }

    

    [JsonConverter(typeof(EmbeddedLiteralConverter<JsonFile>))]

    public JsonFile JsonFile { get; set; }

}

笔记:


严格来说,您的输入 JSON格式不正确。属性的字符串值JsonFile包含未转义的回车符:


 "JsonFile":"      {  

    \"evaluation\":{  

       \"number\":[  

根据最初的 JSON 提议以及JSON RFC 7159 Page 8,必须对此类控制字符进行转义:


 "{\r\n  \"evaluation\": {\r\n    \"number\": ..." 

要确认这一点,您可以将您的初始 JSON 上传到https://jsonformatter.curiousconcept.com/,它会报告以下错误:


无效的 JSON (RFC 4627):错误:发现无效字符。[代码 18,结构 39]


事实证明,Json.NET 会毫无怨言地读取这种无效的 JSON,但只会通过正确转义嵌套 JSON 文字中的回车和换行符来编写格式良好的 JSON。因此,您重新序列化的 JSON 看起来与初始 JSON 不同。但是,它将是格式良好的,并且应该可以被任何 JSON 解析器使用。


为了防止堆栈溢出异常时序列化,EmbeddedLiteralConverter<T>.WriteJson()禁用自身通过使用该技术从递归调用时此答案到JSON.Net使用时抛出StackOverflowException [JsonConvert()]。


工作示例 .Net fiddle在这里。


查看完整回答
反对 回复 2021-11-28
  • 1 回答
  • 0 关注
  • 270 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信