在 JsonConverter.ReadJson() 中有效地获取完整的 json 字符串 [英] Efficiently get full json string in JsonConverter.ReadJson()

查看:27
本文介绍了在 JsonConverter.ReadJson() 中有效地获取完整的 json 字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在 JsonConverter.ReadJson() 中有效地获取完整的 json 字符串?

我能做到:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {var json = JObject.Load(reader).ToString(Formatting.None);

但是这似乎非常低效,因为我无缘无故地序列化和反序列化

还有更好的方法吗?

解决方案

ReadJson() 必须完全解析正在读取的 JSON,以便确认 JSON 格式正确且 JsonReader 正确退出时位于当前值的末尾.但是,无需将整个 JSON 加载到中间 JObject 层次结构中,只需将其重新转换为 JSON 字符串.相反,您可以通过使用 JRaw.Create 获得更好的性能():

var json = JRaw.Create(reader).ToString();

可以在 参考源,此方法直接从传入的 JsonReader 流式传输到 StringWriter - 无需加载到中间 JToken 层次结构并重新序列化 - 通过使用 JsonWriter.WriteToken(JsonReader):

<块引用>

public static JRaw Create(JsonReader reader){使用 (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture))使用 (JsonTextWriter jsonWriter = new JsonTextWriter(sw)){jsonWriter.WriteToken(reader);返回新的 JRaw(sw.ToString());}}

生成的 JRaw 只是将该字符串封装在其 .(当然,不能保证生成的 JSON 代表一个对象,只能代表格式正确的 JSON.)

请注意,JsonTextReader 会自动识别和解析 常用格式DateTime对象,也将浮点值解析为double.如果您需要最字面"的 JSON 字符串,您可能希望抑制 DateTime 识别和/或将浮点值解析为 decimal.以下以 JRaw.Create() 为模型的扩展方法完成了这项工作:

public static string ReadOuterJson(this JsonReader reader, Formatting formatting = Formatting.None, DateParseHandling? dateParseHandling = null, FloatParseHandling? floatParseHandling = null){//如果您希望空 JSON 值返回空字符串,请删除此行:if (reader.TokenType == JsonToken.Null)返回空值;var oldDateParseHandling = reader.DateParseHandling;var oldFloatParseHandling = reader.FloatParseHandling;尝试{if (dateParseHandling != null)reader.DateParseHandling = dateParseHandling.Value;if (floatParseHandling != null)reader.FloatParseHandling = floatParseHandling.Value;使用 (var sw = new StringWriter(CultureInfo.InvariantCulture))使用 (var jsonWriter = new JsonTextWriter(sw) { Formatting =formatting }){jsonWriter.WriteToken(reader);返回 sw.ToString();}}最后{reader.DateParseHandling = oldDateParseHandling;reader.FloatParseHandling = oldFloatParseHandling;}}

然后这样称呼它,例如:

var json = reader.ReadOuterJson(dateParseHandling: DateParseHandling.None);

有关为什么需要这样做的详细信息,请参阅:

How can I efficiently get full json string in JsonConverter.ReadJson() ?

I can do:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
      var json = JObject.Load(reader).ToString(Formatting.None);

However that seems very inefficient as I serialize and deserialize for no reason

Any better way ?

解决方案

ReadJson() must fully parse the JSON being read so that the JSON is confirmed to be well-formed and the JsonReader is correctly positioned at the end of the current value upon exit. However, it is not necessary to load the entire JSON into an intermediate JObject hierarchy simply to re-convert it to a JSON string. Instead, you may be able to get better performance by using JRaw.Create():

var json = JRaw.Create(reader).ToString();

As can be seen in the reference source, this method streams directly from the incoming JsonReader to a StringWriter - without loading into an intermediate JToken hierarchy and re-serializing - by using JsonWriter.WriteToken(JsonReader):

public static JRaw Create(JsonReader reader)
{
    using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture))
    using (JsonTextWriter jsonWriter = new JsonTextWriter(sw))
    {
        jsonWriter.WriteToken(reader);

        return new JRaw(sw.ToString());
    }
}

The resulting JRaw simply encapsulates that string in its Value. (Of course, there is no guarantee that the resulting JSON represents an object, only that it represents well-formed JSON.)

Note that JsonTextReader will automatically recognize and parse dates and times in common formats as DateTime objects, and also parse floating point values as double. If you need the "most literal" JSON string you may want to suppress DateTime recognition and/or parse floating point values as decimal. The following extension method, modeled on JRaw.Create(), does the job:

public static string ReadOuterJson(this JsonReader reader, Formatting formatting = Formatting.None, DateParseHandling? dateParseHandling = null, FloatParseHandling? floatParseHandling = null)
{
    // If you would prefer a null JSON value to return an empty string, remove this line:
    if (reader.TokenType == JsonToken.Null)
        return null;
    var oldDateParseHandling = reader.DateParseHandling;
    var oldFloatParseHandling = reader.FloatParseHandling;
    try
    {
        if (dateParseHandling != null)
            reader.DateParseHandling = dateParseHandling.Value;
        if (floatParseHandling != null)
            reader.FloatParseHandling = floatParseHandling.Value;
        using (var sw = new StringWriter(CultureInfo.InvariantCulture))
        using (var jsonWriter = new JsonTextWriter(sw) { Formatting = formatting })
        {
            jsonWriter.WriteToken(reader);
            return sw.ToString();
        }
    }
    finally
    {
        reader.DateParseHandling = oldDateParseHandling;
        reader.FloatParseHandling = oldFloatParseHandling;
    }
}

And then call it like, e.g.:

var json = reader.ReadOuterJson(dateParseHandling: DateParseHandling.None);

For details on why this may be necessary, see:

这篇关于在 JsonConverter.ReadJson() 中有效地获取完整的 json 字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆