Skip to content

Commit b9367ca

Browse files
committed
Use System.Text.Json
1 parent 1275e46 commit b9367ca

File tree

10 files changed

+404
-90
lines changed

10 files changed

+404
-90
lines changed

samples/Exceptionless.SampleMvc/Exceptionless.SampleMvc.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
</PropertyGroup>
4747
<ItemGroup>
4848
<Reference Include="Microsoft.CSharp" />
49+
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
50+
<HintPath>..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
51+
</Reference>
4952
<Reference Include="System" />
5053
<Reference Include="System.Data" />
5154
<Reference Include="System.Drawing" />
@@ -111,9 +114,6 @@
111114
<Private>True</Private>
112115
<HintPath>..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
113116
</Reference>
114-
<Reference Include="Newtonsoft.Json">
115-
<HintPath>..\..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
116-
</Reference>
117117
<Reference Include="WebGrease">
118118
<Private>True</Private>
119119
<HintPath>..\..\packages\WebGrease.1.6.0\lib\WebGrease.dll</HintPath>

samples/Exceptionless.SampleMvc/packages.config

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.11" targetFramework="net48" />
1818
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net48" />
1919
<package id="Modernizr" version="2.8.3" targetFramework="net48" />
20-
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net48" />
20+
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net48" />
2121
<package id="WebGrease" version="1.6.0" targetFramework="net48" />
2222
</packages>

src/Exceptionless/Exceptionless.csproj

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<Import Project="..\..\build\common.props" />
33

44
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT' ">
@@ -23,12 +23,6 @@
2323
<None Include="readme.txt" pack="true" PackagePath="." />
2424
</ItemGroup>
2525

26-
<ItemGroup Label="Build">
27-
<EmbeddedResource Include="Newtonsoft.Json\Dynamic.snk">
28-
<Link>Exceptionless.Json.Dynamic.snk</Link>
29-
</EmbeddedResource>
30-
</ItemGroup>
31-
3226
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'" Label="Build">
3327
<DefineConstants>$(DefineConstants);NETSTANDARD;NETSTANDARD2_0;HAVE_ADO_NET;HAVE_APP_DOMAIN;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_FORMATTER;HAVE_BINARY_SERIALIZATION;HAVE_BINARY_EXCEPTION_SERIALIZATION;HAVE_CHAR_TO_LOWER_WITH_CULTURE;HAVE_CHAR_TO_STRING_WITH_CULTURE;HAVE_COM_ATTRIBUTES;HAVE_COMPONENT_MODEL;HAVE_CONCURRENT_COLLECTIONS;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DB_NULL_TYPE_CODE;HAVE_DYNAMIC;HAVE_EMPTY_TYPES;HAVE_ENTITY_FRAMEWORK;HAVE_EXPRESSIONS;HAVE_FAST_REVERSE;HAVE_FSHARP_TYPES;HAVE_FULL_REFLECTION;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_ICLONEABLE;HAVE_ICONVERTIBLE;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_MEMORY_BARRIER;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_SECURITY_SAFE_CRITICAL_ATTRIBUTE;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STREAM_READER_WRITER_CLOSE;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TRACE_WRITER;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_XML_DOCUMENT;HAVE_XML_DOCUMENT_TYPE;HAVE_CONCURRENT_DICTIONARY;</DefineConstants>
3428
</PropertyGroup>
@@ -39,8 +33,10 @@
3933
</ItemGroup>
4034

4135
<ItemGroup>
36+
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.0.0" />
4237
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
4338
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
39+
<PackageReference Include="System.Text.Json" Version="5.0.1" />
4440
</ItemGroup>
4541

4642
<PropertyGroup Condition="'$(TargetFramework)' == 'net462'" Label="Build">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Concurrent;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Text.Json;
8+
using System.Text.Json.Serialization;
9+
10+
namespace Exceptionless.Serializer {
11+
12+
internal sealed class DictionaryConverterFactory : JsonConverterFactory {
13+
private readonly static ConcurrentDictionary<Type, JsonConverter> s_lookUp = new();
14+
public override bool CanConvert(Type typeToConvert)
15+
=> typeToConvert.IsGenericType &&
16+
typeToConvert.GetGenericArguments()[0] == typeof(string) &&
17+
IsAssignableToGenericType(typeToConvert, typeof(IDictionary<,>));
18+
19+
public static bool IsAssignableToGenericType(Type givenType, Type genericType) {
20+
var interfaceTypes = givenType.GetInterfaces();
21+
22+
foreach (var it in interfaceTypes) {
23+
if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
24+
return true;
25+
}
26+
27+
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
28+
return true;
29+
30+
Type baseType = givenType.BaseType;
31+
if (baseType == null) return false;
32+
33+
return IsAssignableToGenericType(baseType, genericType);
34+
}
35+
36+
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) {
37+
var lookUp = s_lookUp;
38+
if (lookUp.TryGetValue(typeToConvert, out var value)) return value;
39+
40+
var genericArgs = typeToConvert.GetGenericArguments();
41+
Type elementType = genericArgs[1];
42+
43+
JsonConverter converter = (JsonConverter)Activator.CreateInstance(
44+
typeof(DictionaryConverter<>)
45+
.MakeGenericType(new Type[] { elementType }),
46+
BindingFlags.Instance | BindingFlags.Public,
47+
binder: null,
48+
args: null,
49+
culture: null)!;
50+
51+
lookUp[typeToConvert] = converter;
52+
return converter;
53+
}
54+
}
55+
56+
internal sealed class DictionaryConverter<TValue> : MaxDepthJsonConverter<IDictionary<string, TValue>> {
57+
public override void Write(Utf8JsonWriter writer, IDictionary<string, TValue> value, JsonSerializerOptions options) {
58+
// Skip empty collections
59+
if (value is null || value.Count == 0) return;
60+
61+
writer.WriteStartObject();
62+
63+
var namingPolicy = options.PropertyNamingPolicy;
64+
var naming = namingPolicy as SnakeCaseNamingPolicy;
65+
66+
foreach (var kv in value) {
67+
if (!naming?.IsNameAllowed(kv.Key) ?? true) continue;
68+
69+
// We don't rename dictionary keys
70+
writer.WritePropertyName(kv.Key);
71+
72+
JsonSerializer.Serialize(writer, kv.Value, options);
73+
}
74+
75+
writer.WriteEndObject();
76+
}
77+
}
78+
79+
80+
internal sealed class MaxDepthJsonConverterFactory : JsonConverterFactory {
81+
private readonly static ConcurrentDictionary<Type, JsonConverter> s_lookUp = new();
82+
83+
public override bool CanConvert(Type typeToConvert)
84+
=> !typeToConvert.IsValueType &&
85+
typeToConvert != typeof(object) &&
86+
!typeToConvert.IsArray &&
87+
typeToConvert != typeof(string) &&
88+
!(typeToConvert.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(typeToConvert.GetGenericTypeDefinition())) &&
89+
!typeof(IEnumerable).IsAssignableFrom(typeToConvert);
90+
91+
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) {
92+
var lookUp = s_lookUp;
93+
if (lookUp.TryGetValue(typeToConvert, out var value)) return value;
94+
95+
JsonConverter converter = (JsonConverter)Activator.CreateInstance(
96+
typeof(MaxDepthJsonConverter<>)
97+
.MakeGenericType(new Type[] { typeToConvert }),
98+
BindingFlags.Instance | BindingFlags.Public,
99+
binder: null,
100+
args: null,
101+
culture: null)!;
102+
103+
lookUp[typeToConvert] = converter;
104+
return converter;
105+
}
106+
}
107+
108+
internal class MaxDepthJsonConverter {
109+
private readonly static ConcurrentDictionary<Type, (FieldInfo[], PropertyInfo[])> s_lookUp = new();
110+
111+
public static (FieldInfo[], PropertyInfo[]) GetFieldsAndProperties(Type type) {
112+
var lookUp = s_lookUp;
113+
if (lookUp.TryGetValue(type, out var value)) return value;
114+
115+
value = (
116+
type.GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance).ToArray(),
117+
type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetIndexParameters().Length == 0).ToArray()
118+
);
119+
120+
lookUp[type] = value;
121+
return value;
122+
}
123+
}
124+
125+
internal class MaxDepthJsonConverter<T> : JsonConverter<T> {
126+
public sealed override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
127+
throw new NotImplementedException();
128+
}
129+
130+
public override void Write(Utf8JsonWriter writer, T objectValue, JsonSerializerOptions options) {
131+
writer.WriteStartObject();
132+
133+
var (fields, properties) = MaxDepthJsonConverter.GetFieldsAndProperties(objectValue.GetType());
134+
135+
var namingPolicy = options.PropertyNamingPolicy;
136+
for (int i = 0; i < fields.Length; i++) {
137+
var field = fields[i];
138+
var type = field.FieldType;
139+
if (type.IsClass && !type.IsArray && type != typeof(string) && writer.CurrentDepth >= options.MaxDepth - DefaultJsonSerializer.MaxDepthBuffer) {
140+
continue;
141+
}
142+
var value = field.GetValueDirect(__makeref(objectValue));
143+
if (value is ICollection col && col.Count == 0) {
144+
continue;
145+
}
146+
// TODO: Attribute for name?
147+
if (namingPolicy is SnakeCaseNamingPolicy naming && !naming.IsNameAllowed(field.Name)) {
148+
continue;
149+
}
150+
151+
writer.WritePropertyName(namingPolicy.ConvertName(field.Name));
152+
153+
JsonSerializer.Serialize(writer, value, options);
154+
}
155+
156+
for (int i = 0; i < properties.Length; i++) {
157+
var property = properties[i];
158+
var type = property.PropertyType;
159+
if (!type.IsValueType && !type.IsArray && type != typeof(string) && writer.CurrentDepth >= options.MaxDepth - DefaultJsonSerializer.MaxDepthBuffer) {
160+
continue;
161+
}
162+
var value = property.GetValue(objectValue);
163+
if (value is ICollection col && col.Count == 0) {
164+
continue;
165+
}
166+
// TODO: Attribute for name?
167+
if (namingPolicy is SnakeCaseNamingPolicy naming && !naming.IsNameAllowed(property.Name)) {
168+
continue;
169+
}
170+
171+
writer.WritePropertyName(namingPolicy.ConvertName(property.Name));
172+
JsonSerializer.Serialize(writer, value, options);
173+
}
174+
175+
writer.WriteEndObject();
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)