问题描述
如何在 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);
有关为什么需要这样做的详细信息,请参阅:
Json.NET 在反序列化时解释和修改 ISO 日期JObject #862.
JObject.Parse 修改浮点值的结尾.
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:
Json.NET interprets and modifies ISO dates when deserializing to JObject #862.
JObject.Parse modifies end of floating point values.
这篇关于在 JsonConverter.ReadJson() 中有效地获取完整的 json 字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!