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在这里。
- 1 回答
- 0 关注
- 270 浏览
添加回答
举报