Skip to content

Commit

Permalink
feat(serialization)!: migrate from YamlDotnet to System.Text.Json (#125)
Browse files Browse the repository at this point in the history
  • Loading branch information
VisualBean authored Sep 15, 2023
1 parent 19f62ea commit a0c6d7f
Show file tree
Hide file tree
Showing 63 changed files with 624 additions and 1,598 deletions.
3 changes: 2 additions & 1 deletion src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace LEGO.AsyncAPI.Bindings.Sns
{
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;

Expand All @@ -10,7 +11,7 @@ public class FilterPolicy : IAsyncApiExtensible
/// <summary>
/// A map of a message attribute to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint.
/// </summary>
public IAsyncApiAny Attributes { get; set; }
public AsyncApiAny Attributes { get; set; }

public IDictionary<string, IAsyncApiExtension> Extensions { get; set; } = new Dictionary<string, IAsyncApiExtension>();

Expand Down
1 change: 0 additions & 1 deletion src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Bindings.Sns
{
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;

Expand Down
1 change: 0 additions & 1 deletion src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Bindings.Sns
{
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Models.Any;
using LEGO.AsyncAPI.Readers.ParseNodes;
using LEGO.AsyncAPI.Writers;

Expand Down
1 change: 0 additions & 1 deletion src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ namespace LEGO.AsyncAPI.Bindings.Sns
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Attributes;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;

Expand Down
4 changes: 0 additions & 4 deletions src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ namespace LEGO.AsyncAPI.Bindings.Sqs
{
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;
using Extensions;
using LEGO.AsyncAPI.Readers;
using LEGO.AsyncAPI.Readers.ParseNodes;

public class Queue : IAsyncApiExtensible
{
Expand Down
45 changes: 29 additions & 16 deletions src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
using System;
using System.Linq;
using LEGO.AsyncAPI.Models.Any;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.ParseNodes;
// Copyright (c) The LEGO Group. All rights reserved.

namespace LEGO.AsyncAPI.Bindings
{
using System;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.ParseNodes;

public class StringOrStringList : IAsyncApiElement
{
public StringOrStringList(IAsyncApiAny value)
public StringOrStringList(AsyncApiAny value)
{
this.Value = value switch
this.Value = value.Node switch
{
AsyncApiArray array => IsValidStringList(array) ? array : throw new ArgumentException($"{nameof(StringOrStringList)} value should only contain string items."),
AsyncApiPrimitive<string> => value,
JsonArray array => IsValidStringList(array) ? new AsyncApiAny(array) : throw new ArgumentException($"{nameof(StringOrStringList)} value should only contain string items."),
JsonValue jValue => IsString(jValue) ? new AsyncApiAny(jValue) : throw new ArgumentException($"{nameof(StringOrStringList)} should be a string value or a string list."),
_ => throw new ArgumentException($"{nameof(StringOrStringList)} should be a string value or a string list.")
};
}

public IAsyncApiAny Value { get; }
public AsyncApiAny Value { get; }

public static StringOrStringList Parse(ParseNode node)
{
switch (node)
{
case ValueNode:
return new StringOrStringList(new AsyncApiString(node.GetScalarValue()));
return new StringOrStringList(new AsyncApiAny(node.GetScalarValue()));
case ListNode:
{
var asyncApiArray = new AsyncApiArray();
asyncApiArray.AddRange(node.CreateSimpleList(s => new AsyncApiString(s.GetScalarValue())));
var jsonArray = new JsonArray();
foreach (var item in node as ListNode)
{
jsonArray.Add(item.GetScalarValue());
}

return new StringOrStringList(asyncApiArray);
return new StringOrStringList(new AsyncApiAny(jsonArray));
}

default:
Expand All @@ -40,9 +47,15 @@ public static StringOrStringList Parse(ParseNode node)
}
}

private static bool IsValidStringList(AsyncApiArray array)
private static bool IsString(JsonNode value)
{
var element = JsonDocument.Parse(value.ToJsonString()).RootElement;
return element.ValueKind == JsonValueKind.String;
}

private static bool IsValidStringList(JsonArray array)
{
return array.All(x => x is AsyncApiPrimitive<string>);
return array.All(x => IsString(x));
}
}
}
5 changes: 3 additions & 2 deletions src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace LEGO.AsyncAPI.Readers
using System;
using System.Collections.Generic;
using System.IO;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.Interface;
using LEGO.AsyncAPI.Validations;
Expand Down Expand Up @@ -36,10 +37,10 @@ public class AsyncApiReaderSettings
/// <summary>
/// Dictionary of parsers for converting extensions into strongly typed classes.
/// </summary>
public Dictionary<string, Func<IAsyncApiAny, IAsyncApiExtension>>
public Dictionary<string, Func<AsyncApiAny, IAsyncApiExtension>>
ExtensionParsers
{ get; set; } =
new Dictionary<string, Func<IAsyncApiAny, IAsyncApiExtension>>();
new Dictionary<string, Func<AsyncApiAny, IAsyncApiExtension>>();

public List<IBindingParser<IBinding>>
Bindings
Expand Down
37 changes: 19 additions & 18 deletions src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ namespace LEGO.AsyncAPI.Readers
{
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.Interface;
using YamlDotNet.Core;
using YamlDotNet.RepresentationModel;

/// <summary>
Expand All @@ -35,21 +36,21 @@ public AsyncApiTextReader(AsyncApiReaderSettings settings = null)
/// <returns>Instance of newly created AsyncApiDocument.</returns>
public AsyncApiDocument Read(TextReader input, out AsyncApiDiagnostic diagnostic)
{
YamlDocument yamlDocument;
JsonNode jsonNode;

// Parse the YAML/JSON text in the TextReader into the YamlDocument
try
{
yamlDocument = LoadYamlDocument(input);
jsonNode = LoadYamlDocument(input);
}
catch (YamlException ex)
catch (JsonException ex)
{
diagnostic = new AsyncApiDiagnostic();
diagnostic.Errors.Add(new AsyncApiError($"#line={ex.Start.Line}", ex.Message));
diagnostic.Errors.Add(new AsyncApiError($"#line={ex.LineNumber}", ex.Message));
return new AsyncApiDocument();
}

return new AsyncApiYamlDocumentReader(this.settings).Read(yamlDocument, out diagnostic);
return new AsyncApiJsonDocumentReader(this.settings).Read(jsonNode, out diagnostic);
}

/// <summary>
Expand All @@ -59,25 +60,25 @@ public AsyncApiDocument Read(TextReader input, out AsyncApiDiagnostic diagnostic
/// <returns>A ReadResult instance that contains the resulting AsyncApiDocument and a diagnostics instance.</returns>
public async Task<ReadResult> ReadAsync(TextReader input)
{
YamlDocument yamlDocument;
JsonNode jsonNode;

// Parse the YAML/JSON text in the TextReader into the YamlDocument
try
{
yamlDocument = LoadYamlDocument(input);
jsonNode = LoadYamlDocument(input);
}
catch (YamlException ex)
catch (JsonException ex)
{
var diagnostic = new AsyncApiDiagnostic();
diagnostic.Errors.Add(new AsyncApiError($"#line={ex.Start.Line}", ex.Message));
diagnostic.Errors.Add(new AsyncApiError($"#line={ex.LineNumber}", ex.Message));
return new ReadResult
{
AsyncApiDocument = null,
AsyncApiDiagnostic = diagnostic,
};
}

return await new AsyncApiYamlDocumentReader(this.settings).ReadAsync(yamlDocument);
return await new AsyncApiJsonDocumentReader(this.settings).ReadAsync(jsonNode);
}

/// <summary>
Expand All @@ -90,21 +91,21 @@ public async Task<ReadResult> ReadAsync(TextReader input)
public T ReadFragment<T>(TextReader input, AsyncApiVersion version, out AsyncApiDiagnostic diagnostic)
where T : IAsyncApiElement
{
YamlDocument yamlDocument;
JsonNode jsonNode;

// Parse the YAML/JSON
try
{
yamlDocument = LoadYamlDocument(input);
jsonNode = LoadYamlDocument(input);
}
catch (YamlException ex)
catch (JsonException ex)
{
diagnostic = new AsyncApiDiagnostic();
diagnostic.Errors.Add(new AsyncApiError($"#line={ex.Start.Line}", ex.Message));
diagnostic.Errors.Add(new AsyncApiError($"#line={ex.LineNumber}", ex.Message));
return default;
}

return new AsyncApiYamlDocumentReader(this.settings).ReadFragment<T>(yamlDocument, version,
return new AsyncApiJsonDocumentReader(this.settings).ReadFragment<T>(jsonNode, version,
out diagnostic);
}

Expand All @@ -113,11 +114,11 @@ public T ReadFragment<T>(TextReader input, AsyncApiVersion version, out AsyncApi
/// </summary>
/// <param name="input">Stream containing YAML formatted text.</param>
/// <returns>Instance of a YamlDocument.</returns>
static YamlDocument LoadYamlDocument(TextReader input)
static JsonNode LoadYamlDocument(TextReader input)
{
var yamlStream = new YamlStream();
yamlStream.Load(input);
return yamlStream.Documents.First();
return yamlStream.Documents.First().ToJsonNode();
}
}
}
12 changes: 6 additions & 6 deletions src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,27 @@ namespace LEGO.AsyncAPI.Readers
{
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using LEGO.AsyncAPI.Exceptions;
using LEGO.AsyncAPI.Extensions;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.Interface;
using LEGO.AsyncAPI.Validations;
using YamlDotNet.RepresentationModel;

/// <summary>
/// Service class for converting contents of TextReader into AsyncApiDocument instances.
/// </summary>
internal class AsyncApiYamlDocumentReader : IAsyncApiReader<YamlDocument, AsyncApiDiagnostic>
internal class AsyncApiJsonDocumentReader : IAsyncApiReader<JsonNode, AsyncApiDiagnostic>
{
private readonly AsyncApiReaderSettings settings;

/// <summary>
/// Create stream reader with custom settings if desired.
/// </summary>
/// <param name="settings"></param>
public AsyncApiYamlDocumentReader(AsyncApiReaderSettings settings = null)
public AsyncApiJsonDocumentReader(AsyncApiReaderSettings settings = null)
{
this.settings = settings ?? new AsyncApiReaderSettings();
}
Expand All @@ -35,7 +35,7 @@ public AsyncApiYamlDocumentReader(AsyncApiReaderSettings settings = null)
/// <param name="input">TextReader containing AsyncApi description to parse.</param>
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
/// <returns>Instance of newly created AsyncApiDocument.</returns>
public AsyncApiDocument Read(YamlDocument input, out AsyncApiDiagnostic diagnostic)
public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic)
{
diagnostic = new AsyncApiDiagnostic();
var context = new ParsingContext(diagnostic)
Expand Down Expand Up @@ -76,7 +76,7 @@ public AsyncApiDocument Read(YamlDocument input, out AsyncApiDiagnostic diagnost
return document;
}

public Task<ReadResult> ReadAsync(YamlDocument input)
public Task<ReadResult> ReadAsync(JsonNode input)
{
var diagnostic = new AsyncApiDiagnostic();
var context = new ParsingContext(diagnostic)
Expand Down Expand Up @@ -140,7 +140,7 @@ private void ResolveReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument d
/// <param name="version">Version of the AsyncApi specification that the fragment conforms to.</param>
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
/// <returns>Instance of newly created AsyncApiDocument.</returns>
public T ReadFragment<T>(YamlDocument input, AsyncApiVersion version, out AsyncApiDiagnostic diagnostic)
public T ReadFragment<T>(JsonNode input, AsyncApiVersion version, out AsyncApiDiagnostic diagnostic)
where T : IAsyncApiElement
{
diagnostic = new AsyncApiDiagnostic();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Readers.Exceptions
{
using System;
using LEGO.AsyncAPI.Exceptions;
using YamlDotNet.RepresentationModel;

[Serializable]
public class AsyncApiReaderException : AsyncApiException
Expand All @@ -24,14 +23,6 @@ public AsyncApiReaderException(string message, ParsingContext context)
this.Pointer = context.GetLocation();
}

public AsyncApiReaderException(string message, YamlNode node)
: base(message)
{
// This only includes line because using a char range causes tests to break due to CR/LF & LF differences
// See https://tools.ietf.org/html/rfc5147 for syntax
this.Pointer = $"#line={node.Start.Line}";
}

public AsyncApiReaderException(string message, Exception innerException)
: base(message, innerException)
{
Expand Down
32 changes: 32 additions & 0 deletions src/LEGO.AsyncAPI.Readers/JsonHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) The LEGO Group. All rights reserved.

namespace LEGO.AsyncAPI.Readers
{
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json.Nodes;
using LEGO.AsyncAPI.Exceptions;
using YamlDotNet.RepresentationModel;

internal static class JsonHelper
{
public static string GetScalarValue(this JsonNode node)
{
var scalarNode = node is JsonValue value ? value : throw new AsyncApiException($"Expected scalar value");
return Convert.ToString(scalarNode.GetValue<object>(), CultureInfo.InvariantCulture);
}

public static JsonNode ParseJsonString(string jsonString)
{
return JsonNode.Parse(jsonString);
var reader = new StringReader(jsonString);
var yamlStream = new YamlStream();
yamlStream.Load(reader);

var yamlDocument = yamlStream.Documents.First();
return yamlDocument.RootNode.ToJsonNode();
}
}
}
Loading

0 comments on commit a0c6d7f

Please sign in to comment.