Skip to content

Commit 48a2267

Browse files
authored
Fix Config serialization and empty Config serialization. (#214)
1 parent e7171f6 commit 48a2267

File tree

4 files changed

+53
-37
lines changed

4 files changed

+53
-37
lines changed

src/Hocon.Configuration.Test/SerializationSpecs.cs

+19-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static IEnumerable<object[]> HoconGenerator()
2626
};
2727
}
2828

29-
[Theory(Skip = "Doesn't work right now")]
29+
[Theory]
3030
[MemberData(nameof(HoconGenerator))]
3131
public void ShouldSerializeHocon(string hocon, string fallback1, string fallback2)
3232
{
@@ -42,8 +42,25 @@ public void ShouldSerializeHocon(string hocon, string fallback1, string fallback
4242
private void VerifySerialization(Config config)
4343
{
4444
var serialized = JsonConvert.SerializeObject(config);
45-
var deserialized = (Config)JsonConvert.DeserializeObject(serialized);
45+
var deserialized = JsonConvert.DeserializeObject<Config>(serialized);
4646
config.DumpConfig().Should().Be(deserialized.DumpConfig());
4747
}
48+
49+
[Fact]
50+
public void CanSerializeEmptyConfig()
51+
{
52+
var config = Config.Empty;
53+
var serialized = JsonConvert.SerializeObject(config);
54+
var deserialized = JsonConvert.DeserializeObject<Config>(serialized);
55+
deserialized.IsEmpty.Should().BeTrue();
56+
}
57+
58+
[Fact]
59+
public void ShouldResolveEmptyToEmpty()
60+
{
61+
ConfigurationFactory.Empty.IsEmpty.Should().BeTrue();
62+
ConfigurationFactory.ParseString("{}").IsEmpty.Should().BeTrue();
63+
Config.Empty.IsEmpty.Should().BeTrue();
64+
}
4865
}
4966
}

src/Hocon.Configuration/Config.cs

+33-29
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,32 @@ namespace Hocon
2020
[Serializable]
2121
public class Config : HoconRoot, ISerializable
2222
{
23-
/// <summary>
24-
/// INTERNAL API
25-
///
26-
/// Special case for empty configurations. Immutable and can't be added as a fallback.
27-
/// </summary>
28-
internal sealed class EmptyConfig : Config
29-
{
30-
public static EmptyConfig Instance = new EmptyConfig();
31-
32-
private EmptyConfig() : base(new HoconRoot(new HoconEmptyValue(null)))
33-
{
34-
}
35-
36-
protected override Config Copy(Config fallback = null)
37-
{
38-
return Instance;
39-
}
40-
41-
public override Config WithFallback(Config fallback)
42-
{
43-
return fallback;
44-
}
45-
}
23+
private static readonly HoconValue _emptyValue;
4624

4725
public const string SerializedPropertyName = "_dump";
4826

27+
static Config()
28+
{
29+
_emptyValue = new HoconValue(null);
30+
_emptyValue.Add(new HoconObject(_emptyValue));
31+
}
32+
4933
[Obsolete("For json serialization/deserialization only", true)]
5034
private Config()
5135
{
5236
}
53-
37+
5438
/// <inheritdoc />
5539
/// <summary>
5640
/// Initializes a new instance of the <see cref="Config" /> class.
5741
/// </summary>
42+
private Config(HoconValue value)
43+
{
44+
Value = value;
45+
Root = GetRootValue();
46+
}
47+
48+
/// <inheritdoc cref="Config(HoconValue)" />
5849
private Config(HoconValue value, Config fallback)
5950
{
6051
Fallback = fallback;
@@ -63,15 +54,16 @@ private Config(HoconValue value, Config fallback)
6354
Root = GetRootValue();
6455
}
6556

66-
/// <inheritdoc cref="Config(HoconValue, Config)" />
57+
58+
/// <inheritdoc cref="Config(HoconValue)" />
6759
/// <param name="root">The root node to base this configuration.</param>
6860
/// <exception cref="T:System.ArgumentNullException">"The root value cannot be null."</exception>
6961
public Config(HoconRoot root) : base(root?.Value, root?.Substitutions ?? Enumerable.Empty<HoconSubstitution>())
7062
{
7163
Root = GetRootValue();
7264
}
7365

74-
/// <inheritdoc cref="Config(HoconValue, Config)" />
66+
/// <inheritdoc cref="Config(HoconValue)" />
7567
/// <param name="source">The configuration to use as the primary source.</param>
7668
/// <param name="fallback">The configuration to use as a secondary source.</param>
7769
/// <exception cref="ArgumentNullException">The source configuration cannot be null.</exception>
@@ -89,7 +81,7 @@ public Config(HoconRoot source, Config fallback) : base(source?.Value,
8981
/// <remarks>
9082
/// Added for brevity and API backwards-compatibility with Akka.Hocon.
9183
/// </remarks>
92-
public static Config Empty => ConfigurationFactory.Empty;
84+
public static Config Empty => CreateEmpty();
9385

9486
/// <summary>
9587
/// The configuration used as a secondary source.
@@ -101,6 +93,11 @@ public Config(HoconRoot source, Config fallback) : base(source?.Value,
10193
/// </summary>
10294
public virtual HoconValue Root { get; }
10395

96+
/// <summary>
97+
/// Determines if this root node contains any values
98+
/// </summary>
99+
public virtual bool IsEmpty => Value == _emptyValue;
100+
104101
/// <summary>
105102
/// Returns string representation of <see cref="Config" />, allowing to include fallback values
106103
/// </summary>
@@ -166,7 +163,7 @@ public virtual Config WithFallback(Config fallback)
166163
if (fallback == this)
167164
throw new ArgumentException("Config can not have itself as fallback", nameof(fallback));
168165

169-
if (fallback == Config.Empty)
166+
if (fallback.IsEmpty)
170167
return this; // no-op
171168

172169
// If Fallback is not set - we will set it in new copy
@@ -219,7 +216,14 @@ public override IEnumerable<KeyValuePair<string, HoconField>> AsEnumerable()
219216
used.Add(kvp.Key);
220217
}
221218
}
222-
219+
220+
private static Config CreateEmpty()
221+
{
222+
var value = new HoconValue(null);
223+
value.Add(new HoconObject(value));
224+
return new Config(value);
225+
}
226+
223227
/// <summary>
224228
/// Performs aggregation of Value and all Fallbacks into single <see cref="HoconValue"/> object
225229
/// </summary>

src/Hocon.Configuration/ConfigurationFactory.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public static class ConfigurationFactory
2525
/// <summary>
2626
/// Generates an empty configuration.
2727
/// </summary>
28-
public static Config Empty => Config.EmptyConfig.Instance;
28+
public static Config Empty => Config.Empty;
2929

3030
/// <summary>
3131
/// Generates a configuration defined in the supplied

src/Hocon/HoconRoot.cs

-5
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@ public HoconRoot(HoconValue value, IEnumerable<HoconSubstitution> substitutions)
4949
/// </summary>
5050
public IEnumerable<HoconSubstitution> Substitutions { get; }
5151

52-
/// <summary>
53-
/// Determines if this root node contains any values
54-
/// </summary>
55-
public virtual bool IsEmpty => Value == null || Value.Type == HoconType.Empty;
56-
5752
protected virtual HoconValue GetNode(HoconPath path, bool throwIfNotFound = false)
5853
{
5954
if (Value.Type != HoconType.Object)

0 commit comments

Comments
 (0)