Skip to content

Commit 6a259d9

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

File tree

10 files changed

+403
-90
lines changed

10 files changed

+403
-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,177 @@
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+
writer.WritePropertyName(namingPolicy.ConvertName(kv.Key));
70+
71+
JsonSerializer.Serialize(writer, kv.Value, options);
72+
}
73+
74+
writer.WriteEndObject();
75+
}
76+
}
77+
78+
79+
internal sealed class MaxDepthJsonConverterFactory : JsonConverterFactory {
80+
private readonly static ConcurrentDictionary<Type, JsonConverter> s_lookUp = new();
81+
82+
public override bool CanConvert(Type typeToConvert)
83+
=> !typeToConvert.IsValueType &&
84+
typeToConvert != typeof(object) &&
85+
!typeToConvert.IsArray &&
86+
typeToConvert != typeof(string) &&
87+
!(typeToConvert.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(typeToConvert.GetGenericTypeDefinition())) &&
88+
!typeof(IEnumerable).IsAssignableFrom(typeToConvert);
89+
90+
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) {
91+
var lookUp = s_lookUp;
92+
if (lookUp.TryGetValue(typeToConvert, out var value)) return value;
93+
94+
JsonConverter converter = (JsonConverter)Activator.CreateInstance(
95+
typeof(MaxDepthJsonConverter<>)
96+
.MakeGenericType(new Type[] { typeToConvert }),
97+
BindingFlags.Instance | BindingFlags.Public,
98+
binder: null,
99+
args: null,
100+
culture: null)!;
101+
102+
lookUp[typeToConvert] = converter;
103+
return converter;
104+
}
105+
}
106+
107+
internal class MaxDepthJsonConverter {
108+
private readonly static ConcurrentDictionary<Type, (FieldInfo[], PropertyInfo[])> s_lookUp = new();
109+
110+
public static (FieldInfo[], PropertyInfo[]) GetFieldsAndProperties(Type type) {
111+
var lookUp = s_lookUp;
112+
if (lookUp.TryGetValue(type, out var value)) return value;
113+
114+
value = (
115+
type.GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance).ToArray(),
116+
type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetIndexParameters().Length == 0).ToArray()
117+
);
118+
119+
lookUp[type] = value;
120+
return value;
121+
}
122+
}
123+
124+
internal class MaxDepthJsonConverter<T> : JsonConverter<T> {
125+
public sealed override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
126+
throw new NotImplementedException();
127+
}
128+
129+
public override void Write(Utf8JsonWriter writer, T objectValue, JsonSerializerOptions options) {
130+
writer.WriteStartObject();
131+
132+
var (fields, properties) = MaxDepthJsonConverter.GetFieldsAndProperties(objectValue.GetType());
133+
134+
var namingPolicy = options.PropertyNamingPolicy;
135+
for (int i = 0; i < fields.Length; i++) {
136+
var field = fields[i];
137+
var type = field.FieldType;
138+
if (type.IsClass && !type.IsArray && type != typeof(string) && writer.CurrentDepth >= options.MaxDepth - DefaultJsonSerializer.MaxDepthBuffer) {
139+
continue;
140+
}
141+
var value = field.GetValueDirect(__makeref(objectValue));
142+
if (value is ICollection col && col.Count == 0) {
143+
continue;
144+
}
145+
// TODO: Attribute for name?
146+
if (namingPolicy is SnakeCaseNamingPolicy naming && !naming.IsNameAllowed(field.Name)) {
147+
continue;
148+
}
149+
150+
writer.WritePropertyName(namingPolicy.ConvertName(field.Name));
151+
152+
JsonSerializer.Serialize(writer, value, options);
153+
}
154+
155+
for (int i = 0; i < properties.Length; i++) {
156+
var property = properties[i];
157+
var type = property.PropertyType;
158+
if (!type.IsValueType && !type.IsArray && type != typeof(string) && writer.CurrentDepth >= options.MaxDepth - DefaultJsonSerializer.MaxDepthBuffer) {
159+
continue;
160+
}
161+
var value = property.GetValue(objectValue);
162+
if (value is ICollection col && col.Count == 0) {
163+
continue;
164+
}
165+
// TODO: Attribute for name?
166+
if (namingPolicy is SnakeCaseNamingPolicy naming && !naming.IsNameAllowed(property.Name)) {
167+
continue;
168+
}
169+
170+
writer.WritePropertyName(namingPolicy.ConvertName(property.Name));
171+
JsonSerializer.Serialize(writer, value, options);
172+
}
173+
174+
writer.WriteEndObject();
175+
}
176+
}
177+
}

0 commit comments

Comments
 (0)