Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Winton. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Winton.Extensions.Configuration.Consul.Internals;

namespace Winton.Extensions.Configuration.Consul.Extensions
{
/// <summary>
/// Extra Extesntions for Json Array Parsing.
/// </summary>
public static class ConfigurationbuilderExtensions
{
/// <summary>
/// Adds a JSON Array configuration source to <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="stream">The <see cref="Stream"/> to read the json configuration data from.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddJsonArrayStream(this IConfigurationBuilder builder, Stream stream)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

return builder.Add<JsonArrayStreamConfigurationSource>(s => s.Stream = stream);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Linq;
using System.Net;
using Consul;
using Winton.Extensions.Configuration.Consul.Parsers;

namespace Winton.Extensions.Configuration.Consul.Extensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Winton. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;

namespace Winton.Extensions.Configuration.Consul.Internals
{
internal sealed class JsonArrayStreamConfigurationProvider : StreamConfigurationProvider
{
public JsonArrayStreamConfigurationProvider(JsonArrayStreamConfigurationSource source)
: base(source)
{
}

/// <inheritdoc />
/// <summary>
/// Loads json configuration key/values from a stream into a provider.
/// </summary>
/// <param name="stream">The json <see cref="Stream"/> to load configuration data from.</param>
public override void Load(Stream stream)
{
Data = JsonConfigurationFileParser.Parse(stream);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Winton. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;

namespace Winton.Extensions.Configuration.Consul.Internals
{
/// <summary>
/// Json Array Variant of Config Provider.
/// </summary>
public class JsonArrayStreamConfigurationSource : StreamConfigurationSource
{
/// <summary>
/// Builds the <see cref="JsonStreamConfigurationProvider"/> for this source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>An <see cref="JsonStreamConfigurationProvider"/>.</returns>
public override IConfigurationProvider Build(IConfigurationBuilder builder)
=> new JsonArrayStreamConfigurationProvider(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright (c) Winton. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using Microsoft.Extensions.Configuration;

namespace Winton.Extensions.Configuration.Consul.Internals
{
internal class JsonConfigurationFileParser
{
private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _context = new Stack<string>();
private string? _currentPath;
private string? _dataPrefix;

private JsonConfigurationFileParser(string? prefix = null)
{
_dataPrefix = prefix;
}

public static IDictionary<string, string> Parse(Stream input)
=> new JsonConfigurationFileParser().ParseStream(input);

private IDictionary<string, string> ParseStream(Stream input)
{
_data.Clear();

var jsonDocumentOptions = new JsonDocumentOptions
{
CommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
};

using (var reader = new StreamReader(input))
using (JsonDocument doc = JsonDocument.Parse(reader.ReadToEnd(), jsonDocumentOptions))
{
if (doc.RootElement.ValueKind != JsonValueKind.Object && doc.RootElement.ValueKind != JsonValueKind.Array)
{
throw new FormatException(/*Resources.FormatError_UnsupportedJSONToken(doc.RootElement.ValueKind)*/);
}

switch (doc.RootElement.ValueKind)
{
case JsonValueKind.Object:
VisitElement(doc.RootElement);
break;
case JsonValueKind.Array:
VisitArrayElement(doc.RootElement);
break;
default:
throw new ArgumentOutOfRangeException();
}
}

return _data;
}

private void VisitElement(JsonElement element)
{
foreach (var property in element.EnumerateObject())
{
EnterContext(property.Name);
VisitValue(property.Value);
ExitContext();
}
}

private void VisitArrayElement(JsonElement element)
{
var prefixIdx = 0;
foreach (var arrayElement in element.EnumerateArray())
{
switch (arrayElement.ValueKind)
{
case JsonValueKind.Number:
case JsonValueKind.String:
case JsonValueKind.True:
case JsonValueKind.False:
case JsonValueKind.Null:
EnterContext($"{(_dataPrefix is null ? $"{prefixIdx}" : $"{_dataPrefix}:{prefixIdx}")}");
VisitValue(arrayElement);
ExitContext();
break;
case JsonValueKind.Object:
foreach (var property in arrayElement.EnumerateObject())
{
EnterContext($"{(_dataPrefix is null ? $"{prefixIdx}:" : $"{_dataPrefix}:{prefixIdx}:")}{property.Name}");
VisitValue(property.Value);
ExitContext();
}

break;
default:
throw new NotSupportedException();
}

prefixIdx++;
}
}

private void VisitValue(JsonElement value)
{
switch (value.ValueKind)
{
case JsonValueKind.Object:
VisitElement(value);
break;

case JsonValueKind.Array:
var index = 0;
foreach (var arrayElement in value.EnumerateArray())
{
EnterContext(index.ToString());
VisitValue(arrayElement);
ExitContext();
index++;
}

break;

case JsonValueKind.Number:
case JsonValueKind.String:
case JsonValueKind.True:
case JsonValueKind.False:
case JsonValueKind.Null:
var key = _currentPath;
if (_data.ContainsKey(key ?? string.Empty))
{
throw new FormatException(/*Resources.FormatError_KeyIsDuplicated(key)*/);
}

_data[key ?? string.Empty] = value.ToString();
break;

default:
throw new FormatException(/*Resources.FormatError_UnsupportedJSONToken(value.ValueKind)*/);
}
}

private void EnterContext(string context)
{
_context.Push(context);
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}

private void ExitContext()
{
_context.Pop();
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using Microsoft.Extensions.Configuration;
using Winton.Extensions.Configuration.Consul.Extensions;

namespace Winton.Extensions.Configuration.Consul.Parsers
{
Expand All @@ -18,7 +19,7 @@ public sealed class JsonConfigurationParser : IConfigurationParser
public IDictionary<string, string> Parse(Stream stream)
{
return new ConfigurationBuilder()
.AddJsonStream(stream)
.AddJsonArrayStream(stream)
.Build()
.AsEnumerable()
.ToDictionary(pair => pair.Key, pair => pair.Value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Consul" Version="0.7.2.6" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
<PackageReference Include="Consul" Version="1.6.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.7" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>

Expand Down