如何在JSON对象中转换转义的JSON字符串
本文关键字:JSON 转义 字符串 转换 对象 | 更新日期: 2023-09-27 18:04:06
我正在从一个公共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包含"body"
对象的文字字符串,该字符串实际上是嵌入的双序列化JSON。要将其反序列化为POCO层次结构,而不需要在任何类型中引入中间的string Json
代理属性,您有以下几个选项:
-
你可以使用LINQ对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>();
-
您可以为POCO引入一个通用的
JsonConverter
,对应于每个Body
对象,该对象将传入的嵌入式JSON字符串文字反序列化为字符串,然后将该字符串反序列化为最终的POCO: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>());
或者将其应用于
"body"
对应的属性,如下所示:public class Response { public string info { get; set; } [JsonConverter(typeof(EmbeddedLiteralConverter<Body>))] public Body body { get; set; } }
注意,转换器检查传入的JSON令牌是否为字符串,如果不是,则直接反序列化。因此,当
"body"
JSON是或不是双序列化时,转换器应该是可用的。
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 Response
{
public string info { get; set; }
public Body body { get; set; }
}
public class RootObject
{
public List<Response> responses { get; set; }
}
要将json编码为json字符串转换为Jobject,您总是可以使用下面的技术,
var token = JToken.Parse(text);
var json = JObject.Parse((string) token);
- 你可以将它反序列化成一个中间类,它有一个属性:
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);
}
或者,如果您不想重新序列化对象,则可以构造目标类型的新版本,而不是匿名类型。
这是我基于Sam I am的答案使用的可行解决方案:
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);