Skip to content

Commit 4f1918c

Browse files
committed
Newtonsoft.Json -> System.Text.Json
1 parent b678843 commit 4f1918c

File tree

6 files changed

+141
-91
lines changed

6 files changed

+141
-91
lines changed

src/Serilog.Formatting.Compact.Reader/LogEventReader.cs

+67-62
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616
using System.Diagnostics;
1717
using System.Globalization;
1818
using System.IO;
19-
using Newtonsoft.Json;
20-
using Newtonsoft.Json.Linq;
19+
using System.Text.Json;
2120
using Serilog.Events;
2221
using Serilog.Parsing;
2322
using System.Linq;
@@ -35,19 +34,16 @@ public class LogEventReader : IDisposable
3534
static readonly MessageTemplateParser Parser = new MessageTemplateParser();
3635
static readonly Rendering[] NoRenderings = Array.Empty<Rendering>();
3736
readonly TextReader _text;
38-
readonly JsonSerializer _serializer;
3937

4038
int _lineNumber;
4139

4240
/// <summary>
4341
/// Construct a <see cref="LogEventReader"/>.
4442
/// </summary>
4543
/// <param name="text">Text to read from.</param>
46-
/// <param name="serializer">If specified, a JSON serializer used when converting event documents.</param>
47-
public LogEventReader(TextReader text, JsonSerializer serializer = null)
44+
public LogEventReader(TextReader text)
4845
{
4946
_text = text ?? throw new ArgumentNullException(nameof(text));
50-
_serializer = serializer ?? CreateSerializer();
5147
}
5248

5349
/// <inheritdoc/>
@@ -77,42 +73,60 @@ public bool TryRead(out LogEvent evt)
7773
_lineNumber++;
7874
}
7975

80-
var data = _serializer.Deserialize(new JsonTextReader(new StringReader(line)));
81-
if (!(data is JObject fields))
82-
throw new InvalidDataException($"The data on line {_lineNumber} is not a complete JSON object.");
83-
84-
evt = ReadFromJObject(_lineNumber, fields);
76+
JsonDocument data = null;
77+
try
78+
{
79+
data = JsonDocument.Parse(line);
80+
}
81+
catch (JsonException)
82+
{
83+
}
84+
using (data)
85+
{
86+
if (data == null || data.RootElement.ValueKind != JsonValueKind.Object)
87+
throw new InvalidDataException($"The data on line {_lineNumber} is not a complete JSON object.");
88+
evt = ReadFromJObject(_lineNumber, data.RootElement);
89+
}
8590
return true;
8691
}
8792

93+
8894
/// <summary>
8995
/// Read a single log event from a JSON-encoded document.
9096
/// </summary>
9197
/// <param name="document">The event in compact-JSON.</param>
92-
/// <param name="serializer">If specified, a JSON serializer used when converting event documents.</param>
9398
/// <returns>The log event.</returns>
94-
public static LogEvent ReadFromString(string document, JsonSerializer serializer = null)
99+
public static LogEvent ReadFromString(string document)
95100
{
96101
if (document == null) throw new ArgumentNullException(nameof(document));
97-
98-
serializer ??= CreateSerializer();
99-
var jObject = serializer.Deserialize<JObject>(new JsonTextReader(new StringReader(document)));
100-
return ReadFromJObject(jObject);
101-
102+
JsonDocument data = null;
103+
try
104+
{
105+
data = JsonDocument.Parse(document);
106+
}
107+
catch (JsonException)
108+
{
109+
}
110+
using (data)
111+
{
112+
if (data == null || data.RootElement.ValueKind != JsonValueKind.Object)
113+
throw new ArgumentException($"The document is not a complete JSON object.", nameof(document));
114+
return ReadFromJObject(data.RootElement);
115+
}
102116
}
103117

104118
/// <summary>
105119
/// Read a single log event from an already-deserialized JSON object.
106120
/// </summary>
107121
/// <param name="jObject">The deserialized compact-JSON event.</param>
108122
/// <returns>The log event.</returns>
109-
public static LogEvent ReadFromJObject(JObject jObject)
123+
public static LogEvent ReadFromJObject(in JsonElement jObject)
110124
{
111-
if (jObject == null) throw new ArgumentNullException(nameof(jObject));
125+
if (jObject.ValueKind != JsonValueKind.Object) throw new ArgumentException(nameof(jObject));
112126
return ReadFromJObject(1, jObject);
113127
}
114128

115-
static LogEvent ReadFromJObject(int lineNumber, JObject jObject)
129+
static LogEvent ReadFromJObject(int lineNumber, in JsonElement jObject)
116130
{
117131
var timestamp = GetRequiredTimestampField(lineNumber, jObject, ClefFields.Timestamp);
118132

@@ -146,20 +160,20 @@ static LogEvent ReadFromJObject(int lineNumber, JObject jObject)
146160

147161
var renderings = NoRenderings;
148162

149-
if (jObject.TryGetValue(ClefFields.Renderings, out var r))
150-
{
151-
if (!(r is JArray renderedByIndex))
163+
if (jObject.TryGetProperty(ClefFields.Renderings, out var r))
164+
{
165+
if (!(r.ValueKind == JsonValueKind.Array))
152166
throw new InvalidDataException($"The `{ClefFields.Renderings}` value on line {lineNumber} is not an array as expected.");
153167

154168
renderings = parsedTemplate.Tokens
155169
.OfType<PropertyToken>()
156170
.Where(t => t.Format != null)
157-
.Zip(renderedByIndex, (t, rd) => new Rendering(t.PropertyName, t.Format, rd.Value<string>()))
171+
.Zip(r.EnumerateArray(), (t, rd) => new Rendering(t.PropertyName, t.Format, rd.GetString()))
158172
.ToArray();
159173
}
160174

161175
var properties = jObject
162-
.Properties()
176+
.EnumerateObject()
163177
.Where(f => !ClefFields.All.Contains(f.Name))
164178
.Select(f =>
165179
{
@@ -177,71 +191,62 @@ static LogEvent ReadFromJObject(int lineNumber, JObject jObject)
177191
return new LogEvent(timestamp, level, exception, parsedTemplate, properties, traceId, spanId);
178192
}
179193

180-
static bool TryGetOptionalField(int lineNumber, JObject data, string field, out string value)
194+
static bool TryGetOptionalField(int lineNumber, in JsonElement data, string field, out string value)
181195
{
182-
if (!data.TryGetValue(field, out var token) || token.Type == JTokenType.Null)
196+
if(!data.TryGetProperty(field, out var prop) || prop.ValueKind == JsonValueKind.Null)
183197
{
184198
value = null;
185199
return false;
186200
}
187201

188-
if (token.Type != JTokenType.String)
202+
if (prop.ValueKind != JsonValueKind.String)
189203
throw new InvalidDataException($"The value of `{field}` on line {lineNumber} is not in a supported format.");
190204

191-
value = token.Value<string>();
205+
value = prop.GetString();
192206
return true;
193207
}
194208

195-
static bool TryGetOptionalEventId(int lineNumber, JObject data, string field, out object eventId)
209+
static bool TryGetOptionalEventId(int lineNumber, in JsonElement data, string field, out object eventId)
196210
{
197-
if (!data.TryGetValue(field, out var token) || token.Type == JTokenType.Null)
211+
if (!data.TryGetProperty(field, out var prop) || prop.ValueKind == JsonValueKind.Null)
198212
{
199213
eventId = null;
200214
return false;
201215
}
202216

203-
switch (token.Type)
217+
switch (prop.ValueKind)
204218
{
205-
case JTokenType.String:
206-
eventId = token.Value<string>();
219+
case JsonValueKind.String:
220+
eventId = prop.GetString();
207221
return true;
208-
case JTokenType.Integer:
209-
eventId = token.Value<uint>();
210-
return true;
211-
default:
212-
throw new InvalidDataException(
213-
$"The value of `{field}` on line {lineNumber} is not in a supported format.");
222+
case JsonValueKind.Number:
223+
if (prop.TryGetUInt32(out var v))
224+
{
225+
eventId = v;
226+
return true;
227+
}
228+
break;
214229
}
230+
231+
throw new InvalidDataException(
232+
$"The value of `{field}` on line {lineNumber} is not in a supported format.");
215233
}
216234

217-
static DateTimeOffset GetRequiredTimestampField(int lineNumber, JObject data, string field)
235+
static DateTimeOffset GetRequiredTimestampField(int lineNumber, in JsonElement data, string field)
218236
{
219-
if (!data.TryGetValue(field, out var token) || token.Type == JTokenType.Null)
237+
if(!data.TryGetProperty(field, out var prop) || prop.ValueKind == JsonValueKind.Null)
220238
throw new InvalidDataException($"The data on line {lineNumber} does not include the required `{field}` field.");
221239

222-
if (token.Type == JTokenType.Date)
223-
{
224-
var dt = token.Value<JValue>().Value;
225-
if (dt is DateTimeOffset offset)
226-
return offset;
240+
if (prop.TryGetDateTimeOffset(out var ret))
241+
return ret;
242+
if (prop.TryGetDateTime(out var dt))
243+
return dt;
227244

228-
return (DateTime)dt!;
229-
}
230-
231-
if (token.Type != JTokenType.String)
245+
if (prop.ValueKind != JsonValueKind.String)
232246
throw new InvalidDataException($"The value of `{field}` on line {lineNumber} is not in a supported format.");
233247

234-
var text = token.Value<string>();
248+
var text = prop.GetString();
235249
return DateTimeOffset.Parse(text);
236250
}
237-
238-
static JsonSerializer CreateSerializer()
239-
{
240-
return JsonSerializer.Create(new JsonSerializerSettings
241-
{
242-
DateParseHandling = DateParseHandling.None,
243-
Culture = CultureInfo.InvariantCulture
244-
});
245-
}
246251
}
247252
}

src/Serilog.Formatting.Compact.Reader/PropertyFactory.cs

+49-12
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
using Newtonsoft.Json.Linq;
15+
using System.Text.Json;
1616
using Serilog.Events;
17-
using System.Collections.Generic;
1817
using System.Linq;
1918

2019
namespace Serilog.Formatting.Compact.Reader
@@ -24,7 +23,7 @@ static class PropertyFactory
2423
const string TypeTagPropertyName = "$type";
2524
const string InvalidPropertyNameSubstitute = "(unnamed)";
2625

27-
public static LogEventProperty CreateProperty(string name, JToken value, Rendering[] renderings)
26+
public static LogEventProperty CreateProperty(string name, in JsonElement value, Rendering[] renderings)
2827
{
2928
// The format allows (does not disallow) empty/null property names, but Serilog cannot represent them.
3029
if (!LogEventProperty.IsValidName(name))
@@ -33,25 +32,63 @@ public static LogEventProperty CreateProperty(string name, JToken value, Renderi
3332
return new LogEventProperty(name, CreatePropertyValue(value, renderings));
3433
}
3534

36-
static LogEventPropertyValue CreatePropertyValue(JToken value, Rendering[] renderings)
35+
static LogEventPropertyValue CreatePropertyValue(in JsonElement value, Rendering[] renderings)
3736
{
38-
if (value.Type == JTokenType.Null)
37+
if (value.ValueKind == JsonValueKind.Null)
3938
return new ScalarValue(null);
4039

41-
if (value is JObject obj)
40+
if (value.ValueKind == JsonValueKind.Object)
4241
{
43-
obj.TryGetValue(TypeTagPropertyName, out var tt);
42+
string tts = null;
43+
if(value.TryGetProperty(TypeTagPropertyName, out var tt))
44+
tts = tt.GetString();
4445
return new StructureValue(
45-
obj.Properties().Where(kvp => kvp.Name != TypeTagPropertyName).Select(kvp => CreateProperty(kvp.Name, kvp.Value, null)),
46-
tt?.Value<string>());
46+
value.EnumerateObject().Where(kvp => kvp.Name != TypeTagPropertyName).Select(kvp => CreateProperty(kvp.Name, kvp.Value, null)),
47+
tts);
4748
}
4849

49-
if (value is JArray arr)
50+
if (value.ValueKind == JsonValueKind.Array)
5051
{
51-
return new SequenceValue(arr.Select(v => CreatePropertyValue(v, null)));
52+
return new SequenceValue(value.EnumerateArray().Select(v => CreatePropertyValue(v, null)));
5253
}
5354

54-
var raw = value.Value<JValue>().Value;
55+
object raw = null;
56+
switch(value.ValueKind)
57+
{
58+
case JsonValueKind.String:
59+
raw = value.GetString();break;
60+
case JsonValueKind.True:
61+
case JsonValueKind.False:
62+
raw = value.GetBoolean();break;
63+
case JsonValueKind.Number:
64+
{
65+
if (value.TryGetInt64(out var x))
66+
{
67+
raw = x; break;
68+
}
69+
}
70+
{
71+
if (value.TryGetUInt64(out var x))
72+
{
73+
raw = x; break;
74+
}
75+
}
76+
{
77+
if (value.TryGetDouble(out var x))
78+
{
79+
raw = x; break;
80+
}
81+
}
82+
{
83+
if (value.TryGetDecimal(out var x))
84+
{
85+
raw = x; break;
86+
}
87+
}
88+
break;
89+
}
90+
if(raw == null)
91+
raw = value.GetRawText();
5592

5693
return renderings != null && renderings.Length != 0 ?
5794
new RenderableScalarValue(raw, renderings) :

src/Serilog.Formatting.Compact.Reader/Serilog.Formatting.Compact.Reader.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
</PropertyGroup>
2323

2424
<ItemGroup>
25-
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
25+
<PackageReference Condition=" '$(TargetFramework)' != 'net5.0'" Include="System.Text.Json" Version="7.0.3" />
26+
<PackageReference Condition=" '$(TargetFramework)' == 'net5.0'" Include="System.Text.Json" Version="6.0.8" />
2627
<PackageReference Include="Serilog" Version="3.1.0-*" />
2728
</ItemGroup>
2829

test/Serilog.Formatting.Compact.Reader.Tests/LogEventReaderTests.cs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
using Newtonsoft.Json;
2-
using Newtonsoft.Json.Linq;
3-
using Serilog.Events;
1+
using Serilog.Events;
42
using System;
53
using System.Collections.Generic;
64
using System.IO;
5+
using System.Text.Json;
76
using Xunit;
87

98
namespace Serilog.Formatting.Compact.Reader.Tests
@@ -55,10 +54,12 @@ public void MessagesAreEscapedIntoTemplates()
5554
public void HandlesDefaultJsonNetSerialization()
5655
{
5756
const string document = "{\"@t\":\"2016-10-12T04:20:58.0554314Z\",\"@m\":\"Hello\"}";
58-
var jObject = JsonConvert.DeserializeObject<JObject>(document);
59-
var evt = LogEventReader.ReadFromJObject(jObject);
57+
using (var jd = JsonDocument.Parse(document))
58+
{
59+
var evt = LogEventReader.ReadFromJObject(jd.RootElement);
6060

61-
Assert.Equal(DateTimeOffset.Parse("2016-10-12T04:20:58.0554314Z"), evt.Timestamp);
61+
Assert.Equal(DateTimeOffset.Parse("2016-10-12T04:20:58.0554314Z"), evt.Timestamp);
62+
}
6263
}
6364

6465
[Fact]

0 commit comments

Comments
 (0)