【问题标题】:How to escape embedded JSON after unescape取消转义后如何转义嵌入的 JSON
【发布时间】:2019-01-21 13:04:11
【问题描述】:

当使用 Json.NET 进行序列化时,我需要在反序列化之前取消转义后转义嵌入的 JSON。这意味着我根据this 帖子未转义 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。这意味着它还返回未转义的JsonFile,我再次需要上面的 JSON 格式(转义嵌入的JsonFile)发送到客户端 API。

这是未转义的 JSON 格式,我需要将其转换为上面的转义 JSON(转义嵌入 JsonFile

{  
   "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"
               }
            ]
         }
      }
   }
}

之前我问过question 如何将这种嵌入的 JSON 直接反序列化为 c# 类,但那里的答案没有解释如何以相同的格式重新序列化。我需要将上一个问题的答案扩展到写作。

【问题讨论】:

    标签: c# json json.net escaping


    【解决方案1】:

    您可以通过覆盖JsonConverter.WriteJson() 并执行嵌套序列化,将EmbeddedLiteralConverter&lt;T&gt;this answer 扩展到How do I convert an escaped JSON string within a JSON object?,然后编写生成的字符串文字,例如所以:

    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\":[  
      

      根据original JSON proposalJSON 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&lt;T&gt;.WriteJson() 在使用从this answerJSON.Net throws StackOverflowException when using [JsonConvert()] 的技术递归调用时会禁用自身。

    工作示例 .Net fiddle here.

    【讨论】:

    • 谢谢您,先生。 :) 你解释得很好:) 再次感谢你的努力和友好的回应:)
    猜你喜欢
    • 2021-07-30
    • 1970-01-01
    • 2011-11-03
    • 1970-01-01
    • 2019-03-02
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    相关资源
    最近更新 更多