Skip to content

Commit 52dc9bd

Browse files
committed
remove usage of evil JsonDocument.ParseValue
1 parent 191e139 commit 52dc9bd

File tree

3 files changed

+93
-62
lines changed

3 files changed

+93
-62
lines changed
Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,37 @@
11
using System;
2+
using System.Buffers;
23
using System.Numerics;
4+
using System.Text;
35
using System.Text.Json;
46

57
namespace GraphQL.Client.Serializer.SystemTextJson
68
{
79
public static class ConverterHelperExtensions
810
{
9-
public static object ReadNumber(this JsonElement value)
11+
public static object ReadNumber(this ref Utf8JsonReader reader)
1012
{
11-
if (value.TryGetInt32(out int i))
13+
if (reader.TryGetInt32(out int i))
1214
return i;
13-
else if (value.TryGetInt64(out long l))
15+
else if (reader.TryGetInt64(out long l))
1416
return l;
15-
else if (BigInteger.TryParse(value.GetRawText(), out var bi))
16-
return bi;
17-
else if (value.TryGetDouble(out double d))
18-
return d;
19-
else if (value.TryGetDecimal(out decimal dd))
20-
return dd;
17+
else if (reader.TryGetDouble(out double d))
18+
return reader.TryGetBigInteger(out var bi) && bi != new BigInteger(d)
19+
? bi
20+
: (object)d;
21+
else if (reader.TryGetDecimal(out decimal dd))
22+
return reader.TryGetBigInteger(out var bi) && bi != new BigInteger(dd)
23+
? bi
24+
: (object)dd;
2125

22-
throw new NotImplementedException($"Unexpected Number value. Raw text was: {value.GetRawText()}");
26+
throw new NotImplementedException($"Unexpected Number value. Raw text was: {reader.GetRawString()}");
27+
}
28+
29+
public static bool TryGetBigInteger(this ref Utf8JsonReader reader, out BigInteger bi) => BigInteger.TryParse(reader.GetRawString(), out bi);
30+
31+
public static string GetRawString(this ref Utf8JsonReader reader)
32+
{
33+
var byteArray = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan.ToArray();
34+
return Encoding.UTF8.GetString(byteArray);
2335
}
2436
}
2537
}

src/GraphQL.Client.Serializer.SystemTextJson/ErrorPathConverter.cs

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,43 @@ namespace GraphQL.Client.Serializer.SystemTextJson
99
public class ErrorPathConverter : JsonConverter<ErrorPath>
1010
{
1111

12-
public override ErrorPath Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
13-
{
14-
using var doc = JsonDocument.ParseValue(ref reader);
15-
16-
if (doc?.RootElement == null || doc?.RootElement.ValueKind != JsonValueKind.Array)
17-
{
18-
throw new ArgumentException("This converter can only parse when the root element is a JSON Object.");
19-
}
20-
21-
return new ErrorPath(ReadArray(doc.RootElement));
22-
}
12+
public override ErrorPath Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
13+
new ErrorPath(ReadArray(ref reader));
2314

2415
public override void Write(Utf8JsonWriter writer, ErrorPath value, JsonSerializerOptions options)
2516
=> throw new NotImplementedException(
2617
"This converter currently is only intended to be used to read a JSON object into a strongly-typed representation.");
2718

28-
private IEnumerable<object?> ReadArray(JsonElement value)
19+
private IEnumerable<object?> ReadArray(ref Utf8JsonReader reader)
2920
{
30-
foreach (var item in value.EnumerateArray())
21+
if (reader.TokenType != JsonTokenType.StartArray)
3122
{
32-
yield return ReadValue(item);
23+
throw new JsonException("This converter can only parse when the root element is a JSON Array.");
3324
}
25+
26+
var array = new List<object?>();
27+
28+
while (reader.Read())
29+
{
30+
if (reader.TokenType == JsonTokenType.EndArray)
31+
break;
32+
33+
array.Add(ReadValue(ref reader));
34+
}
35+
36+
return array;
3437
}
3538

36-
private object? ReadValue(JsonElement value)
37-
=> value.ValueKind switch
39+
private object? ReadValue(ref Utf8JsonReader reader)
40+
=> reader.TokenType switch
3841
{
39-
JsonValueKind.Number => value.ReadNumber(),
40-
JsonValueKind.True => true,
41-
JsonValueKind.False => false,
42-
JsonValueKind.String => value.GetString(),
43-
JsonValueKind.Null => null,
44-
JsonValueKind.Undefined => null,
45-
_ => throw new InvalidOperationException($"Unexpected value kind: {value.ValueKind}")
42+
JsonTokenType.None => null,
43+
JsonTokenType.String => reader.GetString(),
44+
JsonTokenType.Number => reader.ReadNumber(),
45+
JsonTokenType.True => true,
46+
JsonTokenType.False => false,
47+
JsonTokenType.Null => null,
48+
_ => throw new InvalidOperationException($"Unexpected token type: {reader.TokenType}")
4649
};
4750
}
4851
}

src/GraphQL.Client.Serializer.SystemTextJson/MapConverter.cs

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,52 +15,68 @@ namespace GraphQL.Client.Serializer.SystemTextJson
1515
/// </remarks>
1616
public class MapConverter : JsonConverter<Map>
1717
{
18-
public override Map Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
19-
{
20-
using var doc = JsonDocument.ParseValue(ref reader);
21-
22-
if (doc?.RootElement == null || doc?.RootElement.ValueKind != JsonValueKind.Object)
23-
{
24-
throw new ArgumentException("This converter can only parse when the root element is a JSON Object.");
25-
}
26-
27-
return ReadDictionary(doc.RootElement, new Map());
28-
}
18+
public override Map Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => ReadDictionary(ref reader, new Map());
2919

3020
public override void Write(Utf8JsonWriter writer, Map value, JsonSerializerOptions options)
3121
=> throw new NotImplementedException(
3222
"This converter currently is only intended to be used to read a JSON object into a strongly-typed representation.");
3323

34-
private TDictionary ReadDictionary<TDictionary>(JsonElement element, TDictionary result)
24+
private static TDictionary ReadDictionary<TDictionary>(ref Utf8JsonReader reader, TDictionary result)
3525
where TDictionary : Dictionary<string, object>
3626
{
37-
foreach (var property in element.EnumerateObject())
27+
if (reader.TokenType != JsonTokenType.StartObject)
28+
throw new JsonException();
29+
30+
while (reader.Read())
3831
{
39-
result[property.Name] = ReadValue(property.Value);
32+
if (reader.TokenType == JsonTokenType.EndObject)
33+
break;
34+
35+
if (reader.TokenType != JsonTokenType.PropertyName)
36+
throw new JsonException();
37+
38+
string key = reader.GetString();
39+
40+
// move to property value
41+
if (!reader.Read())
42+
throw new JsonException();
43+
44+
result.Add(key, ReadValue(ref reader));
4045
}
46+
4147
return result;
4248
}
4349

44-
private IEnumerable<object?> ReadArray(JsonElement value)
50+
private static List<object> ReadArray(ref Utf8JsonReader reader)
4551
{
46-
foreach (var item in value.EnumerateArray())
52+
if (reader.TokenType != JsonTokenType.StartArray)
53+
throw new JsonException();
54+
55+
var result = new List<object>();
56+
57+
while (reader.Read())
4758
{
48-
yield return ReadValue(item);
59+
if (reader.TokenType == JsonTokenType.EndArray)
60+
break;
61+
62+
result.Add(ReadValue(ref reader));
4963
}
50-
}
5164

52-
private object? ReadValue(JsonElement value)
53-
=> value.ValueKind switch
65+
return result;
66+
}
67+
68+
private static object? ReadValue(ref Utf8JsonReader reader)
69+
=> reader.TokenType switch
5470
{
55-
JsonValueKind.Array => ReadArray(value).ToList(),
56-
JsonValueKind.Object => ReadDictionary(value, new Dictionary<string, object>()),
57-
JsonValueKind.Number => value.ReadNumber(),
58-
JsonValueKind.True => true,
59-
JsonValueKind.False => false,
60-
JsonValueKind.String => value.GetString(),
61-
JsonValueKind.Null => null,
62-
JsonValueKind.Undefined => null,
63-
_ => throw new InvalidOperationException($"Unexpected value kind: {value.ValueKind}")
71+
JsonTokenType.StartArray => ReadArray(ref reader).ToList(),
72+
JsonTokenType.StartObject => ReadDictionary(ref reader, new Dictionary<string, object>()),
73+
JsonTokenType.Number => reader.ReadNumber(),
74+
JsonTokenType.True => true,
75+
JsonTokenType.False => false,
76+
JsonTokenType.String => reader.GetString(),
77+
JsonTokenType.Null => null,
78+
JsonTokenType.None => null,
79+
_ => throw new InvalidOperationException($"Unexpected value kind: {reader.TokenType}")
6480
};
6581

6682

0 commit comments

Comments
 (0)