Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON.NET: Minify/Format content without re-parsing

Tags:

json

c#

json.net

Is it possible to minify/format a JSON string using the Newtonsoft JSON.NET library without forcing the system to reparse the code? This is what I have for my methods:

public async Task<string> Minify(string json)
{
    // TODO: Some way to do this without a re-parse?
    var jsonObj = await JsonOpener.GetJsonFromString(json);
    return jsonObj.ToString(Formatting.None);
}

public async Task<string> Beautify(string json)
{
    // TODO: Some way to do this without a re-parse?
    var jsonObj = await JsonOpener.GetJsonFromString(json);
    return FormatJson(jsonObj);
}

private string FormatJson(JToken input)
{
    // We could just do input.ToString(Formatting.Indented), but this allows us
    // to take advantage of JsonTextWriter's formatting options.
    using (var stringWriter = new StringWriter(new StringBuilder()))
    {
        using (var jsonWriter = new JsonTextWriter(stringWriter))
        {
            // Configures indentation character and indentation width
            // (e.g., "indent each level using 2 spaces", or "use tabs")
            ConfigureWriter(jsonWriter);
            var serializer = new JsonSerializer();
            serializer.Serialize(jsonWriter, input);

            return stringWriter.ToString();
        }
    }
}

This code works just fine in small blocks of JSON, but it starts to get bogged down with large blocks of content. If I could just strip out everything without having to go through the parser, it would be much faster, I'd imagine.

If I have to reinvent the wheel and strip out all whitespace or whatnot myself, I will, but I don't know if there any gotchas that come into play.

For that matter, is there another library better suited to this?

EDIT: My bad, JSON does not support comments natively.

like image 467
Ari Roth Avatar asked Jun 20 '17 06:06

Ari Roth


1 Answers

Yes, you can do this using Json.Net. Just connect a JsonTextReader directly to a JsonTextWriter. That way you are reusing the tokenizer logic of the reader and the formatting logic of the writer, but you skip the step of converting the tokens into an intermediate object representation and back (which is the time-consuming part).

Here is how I would break it into helper methods to make it super easy and flexible to use:

public static string Minify(string json)
{
    return ReformatJson(json, Formatting.None);
}

public static string Beautify(string json)
{
    return ReformatJson(json, Formatting.Indented);
}

public static string ReformatJson(string json, Formatting formatting)
{
    using (StringReader stringReader = new StringReader(json))
    using (StringWriter stringWriter = new StringWriter())
    {
        ReformatJson(stringReader, stringWriter, formatting);
        return stringWriter.ToString();
    }
}

public static void ReformatJson(TextReader textReader, TextWriter textWriter, Formatting formatting)
{
    using (JsonReader jsonReader = new JsonTextReader(textReader))
    using (JsonWriter jsonWriter = new JsonTextWriter(textWriter))
    {
        jsonWriter.Formatting = formatting;
        jsonWriter.WriteToken(jsonReader);
    }
}

Here is a short demo: https://dotnetfiddle.net/RevZNU

With this setup you could easily add additional overloads that work on streams, too, if you needed it. For example:

public static void Minify(Stream inputStream, Stream outputStream, Encoding encoding = null)
{
    ReformatJson(inputStream, outputStream, Formatting.None, encoding);
}

public static void Beautify(Stream inputStream, Stream outputStream, Encoding encoding = null)
{
    ReformatJson(inputStream, outputStream, Formatting.Indented, encoding);
}

public static void ReformatJson(Stream inputStream, Stream outputStream, Formatting formatting, Encoding encoding = null)
{
    if (encoding == null)
        encoding = new UTF8Encoding(false);

    const int bufferSize = 1024;
    using (StreamReader streamReader = new StreamReader(inputStream, encoding, true, bufferSize, true))
    using (StreamWriter streamWriter = new StreamWriter(outputStream, encoding, bufferSize, true))
    {
        ReformatJson(streamReader, streamWriter, formatting);
    }
}
like image 196
Brian Rogers Avatar answered Nov 15 '22 06:11

Brian Rogers