Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Add support for trailing commas within Utf8JsonReader #36690

Merged
merged 4 commits into from
Apr 8, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add support for trailing commas with single-segment tests.
  • Loading branch information
ahsonkhan committed Apr 8, 2019
commit 1b6e17360d0ad7cece7aa9da2fd8b4a6f134e4cf
1 change: 1 addition & 0 deletions src/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ public partial struct JsonReaderOptions
private int _dummyPrimitive;
public System.Text.Json.JsonCommentHandling CommentHandling { get { throw null; } set { } }
public int MaxDepth { get { throw null; } set { } }
public bool AllowTrailingCommas { get { throw null; } set { } }
}
public partial struct JsonReaderState
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,12 @@ public int MaxDepth
_maxDepth = value;
}
}

/// <summary>
/// Defines whether an extra comma at the end of a list of JSON values in an object or array
/// are allowed (and ignored) within the JSON payload being read.
/// By default, it's set to false, and the reader will throw a <exception cref="JsonReaderException"/> if it encounters a trailing comma.
/// </summary>
public bool AllowTrailingCommas { get; set; }
}
}
174 changes: 174 additions & 0 deletions src/System.Text.Json/tests/Utf8JsonReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static void DefaultUtf8JsonReader()
Assert.Equal(0, json.CurrentState.BytesConsumed);
Assert.Equal(default, json.CurrentState.Position);
Assert.Equal(0, json.CurrentState.Options.MaxDepth);
Assert.False(json.CurrentState.Options.AllowTrailingCommas);
Assert.Equal(JsonCommentHandling.Disallow, json.CurrentState.Options.CommentHandling);

Assert.False(json.Read());
Expand Down Expand Up @@ -2120,6 +2121,179 @@ private static void TestReadTokenWithExtra(byte[] utf8, JsonCommentHandling comm
});
}

[Theory]
[InlineData("{\"name\": \"value\",}")]
[InlineData("{\"name\": [],}")]
[InlineData("{\"name\": 1,}")]
[InlineData("{\"name\": true,}")]
[InlineData("{\"name\": false,}")]
[InlineData("{\"name\": null,}")]
[InlineData("{\"name\": [{},],}")]
[InlineData("{\"first\" : \"value\", \"name\": [{},], \"last\":2 ,}")]
[InlineData("[\"value\",]")]
[InlineData("[1,]")]
[InlineData("[true,]")]
[InlineData("[false,]")]
[InlineData("[null,]")]
[InlineData("[{},]")]
[InlineData("[{\"name\": [],},]")]
[InlineData("[1, {\"name\": [],},2 , ]")]
public static void JsonWithValidCommas(string jsonString)
{
byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);

{
JsonReaderState state = default;
TrailingCommasHelper(utf8, state, allow: false);
}

{
var state = new JsonReaderState(options: default);
TrailingCommasHelper(utf8, state, allow: false);
}

foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
{
var state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = commentHandling });
TrailingCommasHelper(utf8, state, allow: false);
}

foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
{
var state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = commentHandling, AllowTrailingCommas = true });
var reader = new Utf8JsonReader(utf8, isFinalBlock: true, state);

Assert.True(state.Options.AllowTrailingCommas);
Assert.True(reader.CurrentState.Options.AllowTrailingCommas);

while (reader.Read())
{ }
}
}

[Theory]
[InlineData(",")]
[InlineData(" , ")]
[InlineData("{},")]
[InlineData("[],")]
[InlineData("1,")]
[InlineData("true,")]
[InlineData("false,")]
[InlineData("null,")]
[InlineData("{,}")]
[InlineData("{\"name\": 1,,}")]
[InlineData("[,]")]
[InlineData("[1,,]")]
public static void JsonWithInvalidCommas(string jsonString)
{
byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);

foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
{
var state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = commentHandling });
TrailingCommasHelper(utf8, state, allow: false);
}

foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
{
bool allowTrailingCommas = true;
var state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = commentHandling, AllowTrailingCommas = allowTrailingCommas });
TrailingCommasHelper(utf8, state, allowTrailingCommas);
}
}

private static void TrailingCommasHelper(byte[] utf8, JsonReaderState state, bool allow)
{
var reader = new Utf8JsonReader(utf8, isFinalBlock: true, state);

Assert.Equal(allow, state.Options.AllowTrailingCommas);
Assert.Equal(allow, reader.CurrentState.Options.AllowTrailingCommas);

JsonTestHelper.AssertThrows<JsonReaderException>(reader, (jsonReader) =>
{
while (jsonReader.Read())
{ }
});
}

[Theory]
[InlineData("{\"name\": \"value\"/*comment*/,/*comment*/}")]
[InlineData("{\"name\": []/*comment*/,/*comment*/}")]
[InlineData("{\"name\": 1/*comment*/,/*comment*/}")]
[InlineData("{\"name\": true/*comment*/,/*comment*/}")]
[InlineData("{\"name\": false/*comment*/,/*comment*/}")]
[InlineData("{\"name\": null/*comment*/,/*comment*/}")]
[InlineData("{\"name\": [{},]/*comment*/,/*comment*/}")]
[InlineData("{\"first\" : \"value\", \"name\": [{},], \"last\":2 /*comment*/,/*comment*/}")]
[InlineData("[\"value\"/*comment*/,/*comment*/]")]
[InlineData("[1/*comment*/,/*comment*/]")]
[InlineData("[true/*comment*/,/*comment*/]")]
[InlineData("[false/*comment*/,/*comment*/]")]
[InlineData("[null/*comment*/,/*comment*/]")]
[InlineData("[{}/*comment*/,/*comment*/]")]
[InlineData("[{\"name\": [],}/*comment*/,/*comment*/]")]
[InlineData("[1, {\"name\": [],},2 /*comment*/,/*comment*/ ]")]
public static void JsonWithValidCommasWithComments(string jsonString)
{
byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);

var state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = JsonCommentHandling.Allow });
TrailingCommasHelper(utf8, state, allow: false);

state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip });
TrailingCommasHelper(utf8, state, allow: false);

bool allowTrailingCommas = true;
state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = JsonCommentHandling.Allow, AllowTrailingCommas = allowTrailingCommas });
var reader = new Utf8JsonReader(utf8, isFinalBlock: true, state);

Assert.True(state.Options.AllowTrailingCommas);
Assert.True(reader.CurrentState.Options.AllowTrailingCommas);

while (reader.Read())
{ }

state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = allowTrailingCommas });
reader = new Utf8JsonReader(utf8, isFinalBlock: true, state);

Assert.True(state.Options.AllowTrailingCommas);
Assert.True(reader.CurrentState.Options.AllowTrailingCommas);

while (reader.Read())
{ }
}

[Theory]
[InlineData("/*comment*/ ,/*comment*/")]
[InlineData(" /*comment*/ , /*comment*/ ")]
[InlineData("{}/*comment*/,/*comment*/")]
[InlineData("[]/*comment*/,/*comment*/")]
[InlineData("1/*comment*/,/*comment*/")]
[InlineData("true/*comment*/,/*comment*/")]
[InlineData("false/*comment*/,/*comment*/")]
[InlineData("null/*comment*/,/*comment*/")]
[InlineData("{/*comment*/,/*comment*/}")]
[InlineData("{\"name\": 1/*comment*/,/*comment*/,/*comment*/}")]
[InlineData("[/*comment*/,/*comment*/]")]
[InlineData("[1/*comment*/,/*comment*/,/*comment*/]")]
public static void JsonWithInvalidCommasWithComments(string jsonString)
{
byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);

var state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = JsonCommentHandling.Allow });
TrailingCommasHelper(utf8, state, allow: false);

state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip });
TrailingCommasHelper(utf8, state, allow: false);

bool allowTrailingCommas = true;
state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = JsonCommentHandling.Allow, AllowTrailingCommas = allowTrailingCommas });
TrailingCommasHelper(utf8, state, allow: false);

state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = allowTrailingCommas });
TrailingCommasHelper(utf8, state, allow: false);
}

public static IEnumerable<object[]> TestCases
{
get
Expand Down