3

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 ?

dbc
  • 104,963
  • 20
  • 228
  • 340
kofifus
  • 17,260
  • 17
  • 99
  • 173
  • 1
    If you only care about the JSON itself, why not drop the JsonReader and use a TextReader directly? – Etienne de Martel Jul 09 '19 at 01:07
  • because I need it as part of a JsonConverter to be used as an attribute. Basically I want to use reflection of the type to an interface that have a ToJson and FromJson methods so that some of my objects can define them to serialize/deserialize in custom ways – kofifus Jul 09 '19 at 01:32
  • 1
    You're not really using the converter at all in the above code. – Etienne de Martel Jul 09 '19 at 01:45
  • true. I have an interface: `interface JsonMethods { string ToJson(); T FromJson(string s); }` and I want to call these methods if my class inherites this interface _and_ has my custom JsonConverter that will cann these methods – kofifus Jul 09 '19 at 01:50

1 Answers1

7

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:

dbc
  • 104,963
  • 20
  • 228
  • 340
  • 1
    Thanks! I used this to write a manual json converter - https://stackoverflow.com/questions/51256196/c-sharp-json-net-tojson-fromjson – kofifus Jul 17 '19 at 22:28