Skip to content

Commit 7949d8c

Browse files
author
Oren (electricessence)
committed
First attempt at Utf8Json.
1 parent 3e4b98e commit 7949d8c

3 files changed

Lines changed: 232 additions & 0 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
using System;
2+
using System.Linq;
3+
using Utf8Json;
4+
5+
namespace Open.Serialization.Json.System
6+
{
7+
public static class Extensions
8+
{
9+
public static Func<string, T> GetDeserialize<T>(this IJsonFormatterResolver options)
10+
=> json => JsonSerializer.Deserialize<T>(json, options);
11+
12+
public static Func<T, string> GetSerialize<T>(this IJsonFormatterResolver options)
13+
=> item => JsonSerializer.ToJsonString(item, options);
14+
15+
public static Func<object, string> GetSerialize(this IJsonFormatterResolver options)
16+
=> item => JsonSerializer.ToJsonString(item, options);
17+
18+
public static IJsonSerializer GetSerializer(this IJsonFormatterResolver options)
19+
=> new JsonSerializerInternal(options);
20+
21+
public static IJsonSerializer<T> GetSerializer<T>(this IJsonFormatterResolver options)
22+
=> new JsonSerializerInternal<T>(options);
23+
24+
public static IJsonSerializerFactory GetSerializerFactory(this IJsonFormatterResolver options)
25+
=> new JsonSerializerFactory(options);
26+
27+
public static string Serialize<TValue>(this IJsonFormatterResolver options, TValue value)
28+
=> JsonSerializer.ToJsonString(value, options);
29+
public static string Serialize(this IJsonFormatterResolver options, object value)
30+
=> JsonSerializer.ToJsonString(value, options);
31+
public static TValue Deserialize<TValue>(this IJsonFormatterResolver options, string value)
32+
=> JsonSerializer.Deserialize<TValue>(value, options);
33+
34+
public static JsonSerializerOptions Clone(this JsonSerializerOptions options)
35+
{
36+
var clone = new JsonSerializerOptions
37+
{
38+
AllowTrailingCommas = options.AllowTrailingCommas,
39+
DefaultBufferSize = options.DefaultBufferSize,
40+
DictionaryKeyPolicy = options.DictionaryKeyPolicy,
41+
Encoder = options.Encoder,
42+
IgnoreNullValues = options.IgnoreNullValues,
43+
IgnoreReadOnlyProperties = options.IgnoreReadOnlyProperties,
44+
MaxDepth = options.MaxDepth,
45+
PropertyNameCaseInsensitive = options.PropertyNameCaseInsensitive,
46+
PropertyNamingPolicy = options.PropertyNamingPolicy,
47+
ReadCommentHandling = options.ReadCommentHandling,
48+
WriteIndented = options.WriteIndented
49+
};
50+
51+
foreach (var converter in options.Converters)
52+
clone.Converters.Add(converter);
53+
54+
return clone;
55+
}
56+
57+
public static JsonSerializerOptions SetPropertyNameCaseInsensitive(this JsonSerializerOptions options, bool value)
58+
{
59+
options.PropertyNameCaseInsensitive = value;
60+
return options;
61+
}
62+
63+
public static JsonSerializerOptions SetIgnoreNullValues(this JsonSerializerOptions options, bool value = true)
64+
{
65+
options.IgnoreNullValues = value;
66+
return options;
67+
}
68+
69+
public static JsonSerializerOptions UseUnsafeEncoding(this JsonSerializerOptions options)
70+
{
71+
options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
72+
return options;
73+
}
74+
75+
public static JsonSerializerOptions AddConverter(this JsonSerializerOptions options, JsonConverter converter)
76+
{
77+
options.Converters.Add(converter);
78+
return options;
79+
}
80+
81+
static JsonSerializerOptions RoundDoublesCore(this JsonSerializerOptions options, int maxDecimals)
82+
{
83+
var existing = options.Converters.FirstOrDefault(c => c is JsonConverter<double>);
84+
if (existing == null)
85+
return options.AddConverter(new JsonDoubleRoundingConverter(maxDecimals));
86+
if (existing is JsonDoubleRoundingConverter e && e.Maximum == maxDecimals)
87+
return options;
88+
throw new InvalidOperationException("A specific double converter already exists.");
89+
}
90+
91+
static JsonSerializerOptions RoundNullableDoublesCore(this JsonSerializerOptions options, int maxDecimals)
92+
{
93+
var existing = options.Converters.FirstOrDefault(c => c is JsonConverter<double?>);
94+
if (existing is JsonNullableDoubleConverter && existing.GetType() == typeof(JsonNullableDoubleConverter))
95+
{
96+
options.Converters.Remove(existing);
97+
existing = null;
98+
}
99+
if (existing == null)
100+
return options.AddConverter(new JsonNullableDoubleRoundingConverter(maxDecimals));
101+
if (existing is JsonNullableDoubleRoundingConverter e && e.Maximum == maxDecimals)
102+
return options;
103+
throw new InvalidOperationException("A specific double converter already exists.");
104+
}
105+
106+
public static JsonSerializerOptions RoundDoubles(this JsonSerializerOptions options, int maxDecimals)
107+
=> options
108+
.RoundDoublesCore(maxDecimals)
109+
.RoundNullableDoublesCore(maxDecimals);
110+
111+
public static JsonSerializerOptions NormalizeDecimals(this JsonSerializerOptions options)
112+
{
113+
var existing = options.Converters.FirstOrDefault(c => c is JsonConverter<decimal>);
114+
var existingNullable = options.Converters.FirstOrDefault(c => c is JsonConverter<decimal?>);
115+
116+
if (existing is JsonDecimalConverter && existingNullable is JsonNullableDecimalConverter)
117+
return options;
118+
119+
if (existing == null && existingNullable == null)
120+
return options
121+
.AddConverter(JsonDecimalConverter.Instance)
122+
.AddConverter(JsonNullableDecimalConverter.Instance);
123+
124+
if (existing != null)
125+
throw new InvalidOperationException("A specific decimal converter already exists.");
126+
127+
throw new InvalidOperationException("A specific Nullable<decimal> converter already exists.");
128+
}
129+
130+
static JsonSerializerOptions RoundDecimalsCore(this JsonSerializerOptions options, int maxDecimals)
131+
{
132+
var existing = options.Converters.FirstOrDefault(c => c is JsonConverter<decimal>);
133+
if (existing is JsonDecimalConverter && existing.GetType() == typeof(JsonDecimalConverter))
134+
{
135+
options.Converters.Remove(existing);
136+
existing = null;
137+
}
138+
if (existing == null)
139+
return options.AddConverter(new JsonDecimalRoundingConverter(maxDecimals));
140+
if (existing is JsonDecimalRoundingConverter e && e.Maximum == maxDecimals)
141+
return options;
142+
throw new InvalidOperationException("A specific decimal converter already exists.");
143+
}
144+
145+
static JsonSerializerOptions RoundNullableDecimalsCore(this JsonSerializerOptions options, int maxDecimals)
146+
{
147+
var existing = options.Converters.FirstOrDefault(c => c is JsonConverter<decimal?>);
148+
if (existing is JsonNullableDecimalConverter && existing.GetType() == typeof(JsonNullableDecimalConverter))
149+
{
150+
options.Converters.Remove(existing);
151+
existing = null;
152+
}
153+
if (existing == null)
154+
return options.AddConverter(new JsonNullableDecimalRoundingConverter(maxDecimals));
155+
if (existing is JsonNullableDecimalRoundingConverter e && e.Maximum == maxDecimals)
156+
return options;
157+
throw new InvalidOperationException("A specific Nullable<decimal> converter already exists.");
158+
}
159+
160+
public static JsonSerializerOptions RoundDecimals(this JsonSerializerOptions options, int maxDecimals)
161+
=> options
162+
.RoundDecimalsCore(maxDecimals)
163+
.RoundNullableDecimalsCore(maxDecimals);
164+
}
165+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Threading;
2+
using Utf8Json;
3+
4+
namespace Open.Serialization.Json.System
5+
{
6+
public class JsonSerializerFactory : JsonSerializerFactoryBase
7+
{
8+
static readonly IJsonFormatterResolver DefaultOptions = RelaxedJson.Options();
9+
readonly IJsonFormatterResolver _options;
10+
public JsonSerializerFactory(IJsonFormatterResolver defaultOptions = null)
11+
{
12+
_options = defaultOptions?.Clone() ?? DefaultOptions;
13+
}
14+
15+
JsonSerializerInternal _default;
16+
JsonSerializerInternal _caseSensitive;
17+
JsonSerializerInternal _ignoreCase;
18+
19+
protected override SerializerBase GetDeserializerInternal(bool caseSensitive)
20+
=> caseSensitive
21+
? LazyInitializer.EnsureInitialized(ref _caseSensitive,
22+
() => new JsonSerializerInternal(_options.Clone().SetPropertyNameCaseInsensitive(false)))
23+
: LazyInitializer.EnsureInitialized(ref _ignoreCase,
24+
() => new JsonSerializerInternal(_options.Clone().SetPropertyNameCaseInsensitive(true)));
25+
26+
protected override SerializerBase GetSerializerInternal(IJsonSerializationOptions options)
27+
{
28+
if (options == null)
29+
return LazyInitializer.EnsureInitialized(ref _default, () => new JsonSerializerInternal(_options));
30+
31+
var o = _options.Clone();
32+
o.IgnoreNullValues = options.OmitNull;
33+
o.WriteIndented = options.Indent;
34+
o.DictionaryKeyPolicy = options.CamelCaseKeys ? JsonNamingPolicy.CamelCase : null;
35+
o.PropertyNamingPolicy = options.CamelCaseProperties ? JsonNamingPolicy.CamelCase : null;
36+
37+
return new JsonSerializerInternal(o);
38+
}
39+
}
40+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using Utf8Json;
2+
3+
namespace Open.Serialization.Json.System
4+
{
5+
internal class JsonSerializerInternal : SerializerBase, IJsonSerializer
6+
{
7+
readonly IJsonFormatterResolver _options;
8+
internal JsonSerializerInternal(IJsonFormatterResolver options)
9+
{
10+
_options = options;
11+
}
12+
13+
public override T Deserialize<T>(string value)
14+
=> JsonSerializer.Deserialize<T>(value, _options);
15+
16+
public override string Serialize<T>(T item)
17+
=> JsonSerializer.ToJsonString(item, _options);
18+
}
19+
20+
internal class JsonSerializerInternal<T> : Serializer<T>, IJsonSerializer<T>
21+
{
22+
internal JsonSerializerInternal(IJsonFormatterResolver options)
23+
:base(options.GetDeserialize<T>(), options.GetSerialize<T>())
24+
{
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)