Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I parse JSON with comments using System.Text.Json?

I have some JSON that includes comments (even though comments aren't strictly allowed in the JSON spec.) How can I parse this JSON using System.Text.Json?

The JSON I have received is as folows:

// A person
{
    "Id" : 1 /* Person's ID */,
    "Name" : "Foo" // Person's name
}

When I attempt to load it into a JsonDocument like so:

using var doc = JsonDocument.Parse(jsonString);

I get the following exception:

System.Text.Json.JsonReaderException: '/' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.
   at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
   at System.Text.Json.Utf8JsonReader.ConsumeValue(Byte marker)```

And when I attempt to deserialize with JsonSerializer:

var person = JsonSerializer.Deserialize<Person>(jsonString);

I get a similar exception:

System.Text.Json.JsonException: '/' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
 ---> System.Text.Json.JsonReaderException: '/' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.
   at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
   at System.Text.Json.Utf8JsonReader.ConsumeValue(Byte marker)

How can I parse or deserialize this JSON with System.Text.Json?

like image 420
dbc Avatar asked Dec 22 '19 19:12

dbc


People also ask

How do I deserialize text JSON?

A common way to deserialize JSON is to first create a class with properties and fields that represent one or more of the JSON properties. Then, to deserialize from a string or a file, call the JsonSerializer. Deserialize method.

What is System Text JSON?

Text. Json. Serialization namespace, which contains attributes and APIs for advanced scenarios and customization specific to serialization and deserialization.

Why use System text JSON?

Text. Json Namespace. Provides high-performance, low-allocating, and standards-compliant capabilities to process JavaScript Object Notation (JSON), which includes serializing objects to JSON text and deserializing JSON text to objects, with UTF-8 support built-in.


1 Answers

JSON containing comments can be parsed by System.Text.Json, but by default such JSON is considered invalid, likely since comments are not included in the JSON standard. Support for comments nevertheless can be enabled by modifying the JsonCommentHandling enum in options:

Disallow   0   Doesn't allow comments within the JSON input. 
               Comments are treated as invalid JSON if found, and a JsonException is thrown. 
               This is the default value.

Skip       1   Allows comments within the JSON input and ignores them. 
               The Utf8JsonReader behaves as if no comments are present.

Allow      2   Allows comments within the JSON input and treats them as valid tokens. 
               While reading, the caller can access the comment values.

To enable skipping or loading of comments when reading directly with Utf8JsonReader, set JsonReaderOptions.CommentHandling in one of the Utf8JsonReader constructors, e.g. as follows:

static List<string> GetComments(string jsonString)
{
    var options = new JsonReaderOptions 
    { 
        CommentHandling = JsonCommentHandling.Allow 
    };
    var list = new List<string>();
    var reader = new Utf8JsonReader(new ReadOnlySpan<byte>(Encoding.UTF8.GetBytes(jsonString)), options);
    while (reader.Read())
        if (reader.TokenType == JsonTokenType.Comment)
            list.Add(reader.GetComment());
    return list;
}

When parsing with JsonDocument set JsonDocumentOptions.CommentHandling = JsonCommentHandling.Skip:

var options = new JsonDocumentOptions
{
    CommentHandling = JsonCommentHandling.Skip,
};
using var doc = JsonDocument.Parse(jsonString, options);

When deserializing with JsonSerializer set JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip:

var options = new JsonSerializerOptions
{
    ReadCommentHandling = JsonCommentHandling.Skip
};
var person = JsonSerializer.Deserialize<Person>(jsonString, options);

Note that, as of .NET Core 3.1, JsonDocument and JsonSerializer only support skipping or disallowing of comments; they do not support loading them. If you try to set JsonCommentHandling.Allow for either, you will get an exception:

System.ArgumentOutOfRangeException: Comments cannot be stored in a JsonDocument, only the Skip and Disallow comment handling modes are supported. (Parameter 'value')
System.ArgumentOutOfRangeException: Comments cannot be stored when deserializing objects, only the Skip and Disallow comment handling modes are supported. (Parameter 'value')

(This means that one does not need to manually skip comments when writing a JsonConverter<T>.Read() method, which simplifies comment processing as compared to Newtonsoft where comments are exposed to ReadJson() and must be checked for every time a token is read.)

For more see How to serialize and deserialize JSON in .NET : Allow comments and trailing commas.

Demo fiddle here.

like image 188
dbc Avatar answered Oct 01 '22 10:10

dbc