Skip to content

Commit 34d14c3

Browse files
committed
AutofacJsonTokenConverter starts deserializing from token that is after the last token of type JTokenType.Null because a Null-Token means that the section is in the json file and is set to "null" explicitly.
AutofacJsonTokenConverter skips "null"s when populating the configuration because "null" means that the configuration is missing entirely. The extension methods "RegisterJsonFileConfiguration" has new optional parameter "resolveNewInstanceIfNull" that is set to "true" by default.
1 parent 09d94ae commit 34d14c3

File tree

13 files changed

+273
-58
lines changed

13 files changed

+273
-58
lines changed

src/Thinktecture.Configuration.JsonFile.Autofac/Configuration/AutofacCreationJsonConverter.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ public class AutofacCreationJsonConverter : CustomCreationConverter
1818
public AutofacCreationJsonConverter(Type type, IComponentContext container)
1919
: base(type)
2020
{
21-
if (container == null)
22-
throw new ArgumentNullException(nameof(container));
23-
24-
_container = container;
21+
_container = container ?? throw new ArgumentNullException(nameof(container));
2522
}
2623

2724
/// <inheritdoc />
@@ -30,9 +27,7 @@ public override object Create(Type objectType)
3027
if (objectType == null)
3128
throw new ArgumentNullException(nameof(objectType));
3229

33-
return _container.IsRegisteredWithKey(ContainerBuilderExtensions.ConfigurationRegistrationKey, objectType)
34-
? _container.ResolveKeyed(ContainerBuilderExtensions.ConfigurationRegistrationKey, objectType)
35-
: _container.Resolve(objectType);
30+
return _container.ResolveConfigurationType(objectType);
3631
}
3732
}
3833
}

src/Thinktecture.Configuration.JsonFile.Autofac/Configuration/AutofacJsonTokenConverter.cs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ public AutofacJsonTokenConverter(ILifetimeScope scope, IEnumerable<AutofacJsonTo
2525
{
2626
if (scope == null)
2727
throw new ArgumentNullException(nameof(scope));
28-
if (typesToConvertViaAutofac == null)
29-
throw new ArgumentNullException(nameof(typesToConvertViaAutofac));
3028

3129
_jsonSerializerSettingsProvider = jsonSerializerSettingsProvider;
32-
_converters = typesToConvertViaAutofac.Select(t => new AutofacCreationJsonConverter(t.Type, scope)).ToList();
30+
_converters = typesToConvertViaAutofac?.Select(t => new AutofacCreationJsonConverter(t.Type, scope)).ToList() ?? throw new ArgumentNullException(nameof(typesToConvertViaAutofac));
3331
}
3432

3533
/// <inheritdoc />
@@ -49,21 +47,50 @@ public TConfiguration Convert<TConfiguration>(JToken[] tokens)
4947
serializer.Converters.Add(converter);
5048
}
5149

52-
var mainToken = tokens[0];
53-
var mainConfig = (mainToken == null) ? default(TConfiguration) : mainToken.ToObject<TConfiguration>(serializer);
50+
var lastToken = tokens.LastOrDefault(t => t != null);
5451

55-
if (mainConfig != null)
52+
if (IsNull(lastToken))
53+
return default(TConfiguration);
54+
55+
var startIndex = GetStartIndex(tokens);
56+
var startToken = tokens.Skip(startIndex).First(t => t != null && t.Type != JTokenType.Null);
57+
var config = startToken.ToObject<TConfiguration>(serializer);
58+
59+
if (config != null)
5660
{
57-
for (var i = 1; i < tokens.Length; i++)
61+
for (var i = startIndex + 1; i < tokens.Length; i++)
5862
{
59-
using (var reader = new JTokenReader(tokens[i]))
63+
var token = tokens[i];
64+
65+
if (!IsNull(token))
6066
{
61-
serializer.Populate(reader, mainConfig);
67+
using (var reader = new JTokenReader(token))
68+
{
69+
serializer.Populate(reader, config);
70+
}
6271
}
6372
}
6473
}
6574

66-
return mainConfig;
75+
return config;
76+
}
77+
78+
private int GetStartIndex(JToken[] tokens)
79+
{
80+
for (int i = tokens.Length - 1; i >= 0; i--)
81+
{
82+
var token = tokens[i];
83+
84+
if (token != null && token.Type == JTokenType.Null)
85+
return i + 1;
86+
}
87+
88+
return 0;
89+
}
90+
91+
private bool IsNull(JToken token)
92+
{
93+
return token == null || token.Type == JTokenType.Null;
6794
}
6895
}
6996
}

src/Thinktecture.Configuration.JsonFile.Autofac/Configuration/AutofacJsonTokenConverterType.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ public class AutofacJsonTokenConverterType
2222
/// <param name="type">Type to resolve.</param>
2323
public AutofacJsonTokenConverterType(Type type)
2424
{
25-
if (type == null)
26-
throw new ArgumentNullException(nameof(type));
27-
28-
Type = type;
25+
Type = type ?? throw new ArgumentNullException(nameof(type));
2926
}
3027
}
3128
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Autofac;
7+
8+
// ReSharper disable once CheckNamespace
9+
namespace Thinktecture
10+
{
11+
internal static class ComponentContextExtensions
12+
{
13+
public static T ResolveConfigurationType<T>(this IComponentContext container)
14+
{
15+
return (T) container.ResolveConfigurationType(typeof(T));
16+
}
17+
18+
public static object ResolveConfigurationType(this IComponentContext container, Type objectType)
19+
{
20+
return container.IsRegisteredWithKey(ContainerBuilderExtensions.ConfigurationRegistrationKey, objectType)
21+
? container.ResolveKeyed(ContainerBuilderExtensions.ConfigurationRegistrationKey, objectType)
22+
: container.Resolve(objectType);
23+
}
24+
}
25+
}

src/Thinktecture.Configuration.JsonFile.Autofac/Extensions/ContainerBuilderExtensions.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static void RegisterJsonFileConfigurationProvider(this ContainerBuilder b
3434
throw new ArgumentNullException(nameof(builder));
3535
if (configurationFilePaths == null)
3636
throw new ArgumentNullException(nameof(configurationFilePaths));
37-
if(configurationFilePaths.Length == 0)
37+
if (configurationFilePaths.Length == 0)
3838
throw new ArgumentException("The number of configuration file paths cannot 0.");
3939
if (configurationFilePaths.Any(path => String.IsNullOrWhiteSpace(path)))
4040
throw new ArgumentException("At least one of the configuration file path is empty.", nameof(configurationFilePaths));
@@ -93,17 +93,22 @@ private static void RegisterConverterOnce(ContainerBuilder builder)
9393
/// <typeparam name="T">Type of the configuration</typeparam>
9494
/// <param name="builder">Container builder.</param>
9595
/// <param name="propertyName">Tne name of the json property to use to build the configuration.</param>
96+
/// <param name="resolveNewInstanceIfNull">If <c>true</c> then a new instance of <typeparamref name="T"/> is returned even if the configuration is missing or is <c>null</c>; otherwise Autofac will raise an error.</param>
9697
/// <exception cref="ArgumentNullException">Is thrown if the <paramref name="builder"/> is null.</exception>
9798
/// <returns>Autofac registration builder.</returns>
98-
public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> RegisterJsonFileConfiguration<T>(this ContainerBuilder builder, string propertyName = null)
99+
public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> RegisterJsonFileConfiguration<T>(this ContainerBuilder builder, string propertyName = null, bool resolveNewInstanceIfNull = true)
99100
{
100101
if (builder == null)
101102
throw new ArgumentNullException(nameof(builder));
102103

103104
var selector = String.IsNullOrWhiteSpace(propertyName) ? null : new JsonFileConfigurationSelector(propertyName.Trim());
104105
RegisterTypeOnce<T>(builder);
105106

106-
return builder.Register(context => context.Resolve<IConfigurationProvider<JToken, JToken>>().GetConfiguration<T>(selector));
107+
return builder.Register(context =>
108+
{
109+
var config = context.Resolve<IConfigurationProvider<JToken, JToken>>().GetConfiguration<T>(selector);
110+
return config == null && resolveNewInstanceIfNull ? context.ResolveConfigurationType<T>() : config;
111+
});
107112
}
108113

109114
/// <summary>
@@ -113,9 +118,10 @@ public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationSty
113118
/// <param name="builder">Container builder.</param>
114119
/// <param name="registrationKey">The key of a <see cref="IConfigurationProvider{TRawDataIn,TRawDataOut}"/>.</param>
115120
/// <param name="propertyName">Tne name of the json property to use to build the configuration.</param>
121+
/// <param name="resolveNewInstanceIfNull">If <c>true</c> then a new instance of <typeparamref name="T"/> is returned even if the configuration is missing or is <c>null</c>; otherwise Autofac will raise an error.</param>
116122
/// <exception cref="ArgumentNullException">Is thrown if the <paramref name="builder"/> or the <paramref name="registrationKey"/> is null.</exception>
117123
/// <returns>Autofac registration builder.</returns>
118-
public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> RegisterJsonFileConfiguration<T>(this ContainerBuilder builder, AutofacConfigurationProviderKey registrationKey, string propertyName = null)
124+
public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> RegisterJsonFileConfiguration<T>(this ContainerBuilder builder, AutofacConfigurationProviderKey registrationKey, string propertyName = null, bool resolveNewInstanceIfNull = true)
119125
{
120126
if (builder == null)
121127
throw new ArgumentNullException(nameof(builder));
@@ -125,7 +131,11 @@ public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationSty
125131
var selector = String.IsNullOrWhiteSpace(propertyName) ? null : new JsonFileConfigurationSelector(propertyName.Trim());
126132
RegisterTypeOnce<T>(builder);
127133

128-
return builder.Register(context => context.ResolveKeyed<IConfigurationProvider<JToken, JToken>>(registrationKey).GetConfiguration<T>(selector));
134+
return builder.Register(context =>
135+
{
136+
var config = context.ResolveKeyed<IConfigurationProvider<JToken, JToken>>(registrationKey).GetConfiguration<T>(selector);
137+
return config == null && resolveNewInstanceIfNull ? context.ResolveConfigurationType<T>() : config;
138+
});
129139
}
130140

131141
/// <summary>

src/Thinktecture.Configuration.JsonFile/Configuration/CustomCreationConverter.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ public abstract class CustomCreationConverter : JsonConverter
1616
/// <exception cref="ArgumentNullException">Thrown if the provided <paramref name="type"/> is <c>null</c>.</exception>
1717
protected CustomCreationConverter(Type type)
1818
{
19-
if (type == null)
20-
throw new ArgumentNullException(nameof(type));
21-
22-
_type = type;
19+
_type = type ?? throw new ArgumentNullException(nameof(type));
2320
}
2421

2522
/// <summary>

src/Thinktecture.Configuration.JsonFile/Configuration/JsonFileConfigurationLoader.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,14 @@ public class JsonFileConfigurationLoader : IConfigurationLoader<JToken, JToken>
3333
/// <param name="jsonConfigurationProviderFactory">A factory for creation of <see cref="IConfigurationProvider{TRawDataIn,TRawDataOut}"/>.</param>
3434
public JsonFileConfigurationLoader(IFile file, IJsonTokenConverter tokenConverter, string[] filePaths, IEncoding encoding = null, Func<JsonSerializerSettings> jsonSerializerSettingsProvider = null, Func<JToken[], IJsonTokenConverter, IConfigurationProvider<JToken, JToken>> jsonConfigurationProviderFactory = null)
3535
{
36-
if (file == null)
37-
throw new ArgumentNullException(nameof(file));
3836
if (filePaths == null)
3937
throw new ArgumentNullException(nameof(filePaths));
40-
if (tokenConverter == null)
41-
throw new ArgumentNullException(nameof(tokenConverter));
4238
if (filePaths.Any(path => String.IsNullOrWhiteSpace(path)))
4339
throw new ArgumentException("At least one of the configuration file path is empty.", nameof(filePaths));
4440

45-
_file = file;
41+
_file = file ?? throw new ArgumentNullException(nameof(file));
4642
_filePaths = filePaths;
47-
_tokenConverter = tokenConverter;
43+
_tokenConverter = tokenConverter ?? throw new ArgumentNullException(nameof(tokenConverter));
4844
_jsonSerializerSettingsProvider = jsonSerializerSettingsProvider;
4945
_encoding = encoding ?? new UTF8Encoding(false, true).ToInterface();
5046
_jsonConfigurationProviderFactory = jsonConfigurationProviderFactory ?? ((tokens, converter) => new JsonFileConfigurationProvider(tokens, converter));

src/Thinktecture.Configuration.JsonFile/Configuration/JsonFileConfigurationProvider.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@ public JsonFileConfigurationProvider(JToken[] tokens, IJsonTokenConverter tokenC
2121
{
2222
if (tokens == null)
2323
throw new ArgumentNullException(nameof(tokens));
24-
if (tokenConverter == null)
25-
throw new ArgumentNullException(nameof(tokenConverter));
2624
if (tokens.Length == 0)
2725
throw new ArgumentException("Token collection can not be empty.", nameof(tokens));
2826

2927
_tokens = tokens;
30-
_tokenConverter = tokenConverter;
28+
_tokenConverter = tokenConverter ?? throw new ArgumentNullException(nameof(tokenConverter));
3129
}
3230

3331
/// <inheritdoc />

0 commit comments

Comments
 (0)