Skip to content

Commit 6e73851

Browse files
authored
Use SystemTextJson instead of NewtonsoftJson (#12)
* Added support for SystemTextJson serializer * Fixed nullability warnings * Split NewtonsoftJson as addon package
1 parent 19ed009 commit 6e73851

23 files changed

+867
-503
lines changed

ObjectConfigurationExtensions.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7D89563A
1111
EndProject
1212
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Objects", "tests\Tests.Objects\Tests.Objects.csproj", "{06A9F0F7-10D7-4531-A346-2A18C5402C54}"
1313
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Objects.NewtonsoftJson", "src\Objects.NewtonsoftJson\Objects.NewtonsoftJson.csproj", "{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}"
15+
EndProject
1416
Global
1517
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1618
Debug|Any CPU = Debug|Any CPU
@@ -48,9 +50,22 @@ Global
4850
{06A9F0F7-10D7-4531-A346-2A18C5402C54}.Release|x64.Build.0 = Release|Any CPU
4951
{06A9F0F7-10D7-4531-A346-2A18C5402C54}.Release|x86.ActiveCfg = Release|Any CPU
5052
{06A9F0F7-10D7-4531-A346-2A18C5402C54}.Release|x86.Build.0 = Release|Any CPU
53+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Debug|Any CPU.Build.0 = Debug|Any CPU
55+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Debug|x64.ActiveCfg = Debug|Any CPU
56+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Debug|x64.Build.0 = Debug|Any CPU
57+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Debug|x86.ActiveCfg = Debug|Any CPU
58+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Debug|x86.Build.0 = Debug|Any CPU
59+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Release|Any CPU.ActiveCfg = Release|Any CPU
60+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Release|Any CPU.Build.0 = Release|Any CPU
61+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Release|x64.ActiveCfg = Release|Any CPU
62+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Release|x64.Build.0 = Release|Any CPU
63+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Release|x86.ActiveCfg = Release|Any CPU
64+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39}.Release|x86.Build.0 = Release|Any CPU
5165
EndGlobalSection
5266
GlobalSection(NestedProjects) = preSolution
5367
{A5A06279-A908-4B4D-A91B-919BDD4E2957} = {49A4B4B5-2EF8-422F-BC13-1622A3131D85}
5468
{06A9F0F7-10D7-4531-A346-2A18C5402C54} = {7D89563A-B6B2-46E0-A640-E34CC6A2078A}
69+
{9D57A59B-F955-4E36-ACEA-37C0D86C4A39} = {49A4B4B5-2EF8-422F-BC13-1622A3131D85}
5570
EndGlobalSection
5671
EndGlobal
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Linq;
5+
using Microsoft.Extensions.Configuration;
6+
using Newtonsoft.Json;
7+
using Newtonsoft.Json.Linq;
8+
9+
namespace Kralizek.Extensions.Configuration.Internal;
10+
11+
public class NewtonsoftJsonConfigurationSerializer : IConfigurationSerializer
12+
{
13+
public IDictionary<string, string?> Serialize(object source, string rootSectionName)
14+
{
15+
var json = JsonConvert.SerializeObject(source);
16+
var jsonConfig = JObject.Parse(json);
17+
18+
var visitor = new JsonVisitor();
19+
20+
return visitor.ParseObject(jsonConfig, rootSectionName);
21+
}
22+
23+
private class JsonVisitor
24+
{
25+
private readonly IDictionary<string, string?> _data = new SortedDictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
26+
private readonly Stack<string> _context = new ();
27+
private string _currentPath = null!;
28+
29+
public IDictionary<string, string?> ParseObject(JObject jsonObject, string rootSectionName)
30+
{
31+
if (rootSectionName != "")
32+
{
33+
EnterContext(rootSectionName);
34+
}
35+
36+
VisitJObject(jsonObject);
37+
38+
if (rootSectionName != "")
39+
{
40+
ExitContext();
41+
}
42+
43+
return _data;
44+
}
45+
46+
private void VisitJObject(JObject? jObject)
47+
{
48+
foreach (var property in jObject?.Properties() ?? [])
49+
{
50+
EnterContext(property.Name);
51+
VisitProperty(property);
52+
ExitContext();
53+
}
54+
}
55+
56+
private void VisitProperty(JProperty property)
57+
{
58+
VisitToken(property.Value);
59+
}
60+
61+
private void VisitToken(JToken token)
62+
{
63+
switch (token.Type)
64+
{
65+
case JTokenType.Object:
66+
VisitJObject(token.Value<JObject>());
67+
break;
68+
69+
case JTokenType.Array:
70+
VisitArray(token.Value<JArray>());
71+
break;
72+
73+
case JTokenType.Integer:
74+
case JTokenType.Float:
75+
case JTokenType.String:
76+
case JTokenType.Boolean:
77+
case JTokenType.Bytes:
78+
case JTokenType.Raw:
79+
case JTokenType.Null:
80+
VisitPrimitive(token.Value<JValue>());
81+
break;
82+
83+
case JTokenType.None:
84+
case JTokenType.Constructor:
85+
case JTokenType.Property:
86+
case JTokenType.Comment:
87+
case JTokenType.Undefined:
88+
case JTokenType.Date:
89+
case JTokenType.Guid:
90+
case JTokenType.Uri:
91+
case JTokenType.TimeSpan:
92+
default:
93+
throw new NotSupportedException($"Unsupported JSON token '{token.Type}' was found");
94+
}
95+
}
96+
97+
private void VisitArray(JArray? array)
98+
{
99+
if (array is null) return;
100+
101+
for (var index = 0; index < array.Count; index++)
102+
{
103+
EnterContext(index.ToString());
104+
VisitToken(array[index]);
105+
ExitContext();
106+
}
107+
}
108+
109+
private void VisitPrimitive(JValue? data)
110+
{
111+
if (data is null) return;
112+
113+
var key = _currentPath;
114+
115+
if (_data.ContainsKey(key))
116+
{
117+
throw new FormatException($"A duplicate key '{key}' was found.");
118+
}
119+
120+
var stringValue = data.ToString(CultureInfo.InvariantCulture);
121+
122+
if (!string.IsNullOrEmpty(stringValue))
123+
{
124+
_data[key] = stringValue;
125+
}
126+
}
127+
128+
private void EnterContext(string context)
129+
{
130+
_context.Push(context);
131+
_currentPath = ConfigurationPath.Combine(_context.Reverse());
132+
}
133+
134+
private void ExitContext()
135+
{
136+
_context.Pop();
137+
_currentPath = ConfigurationPath.Combine(_context.Reverse());
138+
}
139+
}
140+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using Kralizek.Extensions.Configuration.Internal;
3+
4+
// ReSharper disable CheckNamespace
5+
6+
namespace Microsoft.Extensions.Configuration;
7+
8+
public static class NewtonsoftJsonObjectConfigurationExtensions
9+
{
10+
public static IConfigurationBuilder AddObjectWithNewtonsoftJson(this IConfigurationBuilder configurationBuilder, object? objectToAdd, string? rootSectionName = "")
11+
{
12+
var serializer = new NewtonsoftJsonConfigurationSerializer();
13+
14+
return configurationBuilder.AddObject(serializer, objectToAdd, rootSectionName);
15+
}
16+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<RootNamespace>Kralizek.Extensions.Configuration</RootNamespace>
5+
<AssemblyName>$(RootNamespace).$(MSBuildProjectName)</AssemblyName>
6+
<LangVersion>latest</LangVersion>
7+
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
8+
<Nullable>enable</Nullable>
9+
</PropertyGroup>
10+
11+
<PropertyGroup>
12+
<Authors>Renato Golia</Authors>
13+
</PropertyGroup>
14+
15+
<PropertyGroup>
16+
<PackageId>$(AssemblyName)</PackageId>
17+
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
18+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
19+
<PackageProjectUrl>https://github.com/Kralizek/ObjectConfigurationExtensions</PackageProjectUrl>
20+
<PackageTags>dotnet-standard;microsoft;extensions;configuration;objects;</PackageTags>
21+
<Description>Extension of Kralizek.Extensions.Configuration using Newtonsoft.Json.</Description>
22+
</PropertyGroup>
23+
24+
<PropertyGroup>
25+
<PublishRepositoryUrl>true</PublishRepositoryUrl>
26+
<EmbedUntrackedSources>true</EmbedUntrackedSources>
27+
<IncludeSymbols>true</IncludeSymbols>
28+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
29+
</PropertyGroup>
30+
31+
<ItemGroup>
32+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
33+
<PackageReference Include="SourceLink.Copy.PdbFiles" Version="2.8.3" PrivateAssets="All" />
34+
</ItemGroup>
35+
36+
<ItemGroup>
37+
<ProjectReference Include="..\Objects\Objects.csproj" />
38+
</ItemGroup>
39+
<ItemGroup>
40+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
41+
</ItemGroup>
42+
43+
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using System.Collections.Generic;
2+
3+
namespace Kralizek.Extensions.Configuration.Internal;
4+
5+
public interface IConfigurationSerializer
6+
{
7+
IDictionary<string, string?> Serialize(object source, string rootSectionName);
8+
}

src/Objects/Internal/JsonConfigurationSerializer.cs

Lines changed: 0 additions & 133 deletions
This file was deleted.

0 commit comments

Comments
 (0)