如何在 JSON 对象中转换转义的 JSON 字符串?

Posted

技术标签:

【中文标题】如何在 JSON 对象中转换转义的 JSON 字符串?【英文标题】:How do I convert an escaped JSON string within a JSON object? 【发布时间】:2017-01-02 09:40:27 【问题描述】:

我从一个公共 API 接收一个 JSON 对象,该对象的属性本身就是一个转义的 JSON 字符串。


   "responses":[
      
         "info":"keep \"this\" in a string",
         "body":"\"error\":\"message\":\"Invalid command\",\"type\":\"Exception\",\"code\":123"
      ,
      
         "info":"more \"data\" to keep in a string",
         "body":"\"error\":\"message\":\"Other error\",\"type\":\"Exception\",\"code\":321"
      
   ]

如何将此属性转换为实际的 JSON 对象(未转义),以便使用 NewtonSoft Json.NET 反序列化整个响应?

【问题讨论】:

是否要将正文转换为 JSON?它已经在 J​​SON 中 我尝试过使用 Newtonsoft Json.NET 进行反序列化,但在尝试将主体转换为类时出现错误。 "Error converting value [JSON String] to type '[MyClass]'. Path 'responses[0].body', line 1, position 641." 查看我的编辑。我修好了 【参考方案1】:

您的 JSON 包含实际嵌入的双序列化 JSON 的 "body" 对象的文字字符串。要将其反序列化为 POCO 层次结构,而不需要在任何类型中引入中间 string Json 代理属性,您有以下几种选择:

    您可以使用 LINQ to JSON 预处理您的 JSON,并将文字 "body" 字符串替换为其解析后的等价物:

        var rootToken = JToken.Parse(json);
        foreach (var token in rootToken.SelectTokens("responses[*].body").ToList().Where(t => t.Type == JTokenType.String))
        
            token.Replace(JToken.Parse((string)token));
        
    
        var root = rootToken.ToObject<RootObject>();
    

    您可以为与每个 Body 对象对应的 POCO 引入通用 JsonConverter,该对象将传入的嵌入 JSON 字符串文字解析为 LINQ to JSON 层次结构,然后对其进行反序列化:

    public class EmbeddedLiteralConverter<T> : JsonConverter
    
        public override bool CanConvert(Type objectType)
        
            return typeof(T).IsAssignableFrom(objectType);
        
    
        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;
        
    
        public override bool CanWrite  get  return false;  
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        
            throw new NotImplementedException();
        
    
    

    然后像这样使用它:

        var root = JsonConvert.DeserializeObject<RootObject>(json, new EmbeddedLiteralConverter<Body>());
    

    请注意,转换器会检查传入的 JSON 令牌是否为字符串,如果不是,则直接反序列化。因此,当"body" JSON 被双序列化和没有被双序列化时,转换器应该是可用的。

出于测试目的,我使用http://json2csharp.com/ 生成了以下目标类:

public class Error

    public string message  get; set; 
    public string type  get; set; 
    public int code  get; set; 


public class Body

    public Error error  get; set; 


public class Respons

    public string info  get; set; 
    public Body body  get; set; 


public class RootObject

    public List<Respons> responses  get; set; 

【讨论】:

【参考方案2】:

要将编码为 json 字符串的 json 转换为 Jobject,您始终可以使用以下技术,

var token = JToken.Parse(text);
var json = JObject.Parse((string) token);

【讨论】:

【参考方案3】:
    您可以将其反序列化为具有属性的中间类:string Body get; set; 将“body”字符串反序列化为合适的类型 创建代表目标模型的类的新实例。 序列化该模型

这是一个使用动态类型和匿名对象的程序。

static void Main(string[] args)

    var json = File.ReadAllText("JsonFile1.json");
    dynamic obj = JsonConvert.DeserializeObject(json);

    var dest = new
    
        responses = ((IEnumerable<dynamic>)obj.responses).Select(x => new
        
            info = x.info,
            body = JsonConvert.DeserializeObject((string)x.body)
        )
    ;

    var destJson = JsonConvert.SerializeObject(dest);
    File.WriteAllText("JsonFile2.json", destJson);

或者,如果您不想重新序列化 josn.xml,则可以构建目标类型的新版本,而不是匿名类型。

【讨论】:

@downvoter 我的回答不正确吗?像 OP 这样已经可以使用 JSON.Net 的人是否无法使用我的答案? 看起来我们都被否决了。我希望我能找到一种方法来清理 JSON,而无需为所有不同的 API 响应创建重复的类(这是一个更复杂的简化示例),我可以使用通用的东西来解析值。【参考方案4】:

这是我根据Sam I am's answer 使用的可行解决方案:

dynamic obj = JsonConvert.DeserializeObject(json);
foreach (var response in (IEnumerable<dynamic>)obj.responses)

    response.body = JsonConvert.DeserializeObject((string)response.body);

string result = JsonConvert.SerializeObject(obj);

【讨论】:

以上是关于如何在 JSON 对象中转换转义的 JSON 字符串?的主要内容,如果未能解决你的问题,请参考以下文章

php 数组转换json中括号转义

idea还原转义json

JQuery .ajax 返回json格式,浏览器显示带有转义字符问题

如何利用fastjson将JSON格式的字符串转换为Map,再返回至前端成为js对象

Python - 将带有转义字符的字符串转换为 json

服务器返回的json字符串,客户端解析为毛带转义字符“/”