Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e20a62d
Add core System.Text.Json support
Turnerj Mar 22, 2021
60dc367
Updated benchmarks to use S.T.J
Turnerj Mar 22, 2021
2bf322e
Updated tests to use S.T.J
Turnerj Mar 22, 2021
49cf046
Use 6.0.0-preview.2 of S.T.J
Turnerj Mar 22, 2021
fefb1aa
Use correct attribute for tests
Turnerj Mar 22, 2021
e0699e6
Removed unused using
Turnerj Mar 22, 2021
78a860d
Fixed support for time of day
Turnerj Mar 22, 2021
57ed1cf
Added tests for extended type converters
Turnerj Mar 22, 2021
c409839
Update S.T.J to v6.0.0-preview.5
Turnerj Jun 19, 2021
e7e79bf
Switched DataMember to JsonPropertyName
Turnerj Jul 31, 2021
8a091ac
Updated to S.T.J Preview 6
Turnerj Jul 31, 2021
b442944
Update to S.T.J Preview 7
Turnerj Aug 12, 2021
d846641
Re-add JSON property order
Turnerj Aug 12, 2021
04b27ef
Ignore casing for escape test
Turnerj Aug 12, 2021
ea066db
Allow trailing commas
Turnerj Aug 12, 2021
7705f71
Apply serializer settings for deserialization
Turnerj Aug 12, 2021
8e20564
Support EnumMember for schema enum serialization
Turnerj Aug 13, 2021
5f1f2f0
Decimal and double values to truncate trailing zeroes
Turnerj Aug 13, 2021
5886f88
Fixing build warnings and errors
Turnerj Aug 14, 2021
99e9570
Force serialization of non-public setter for JsonLdContext
Turnerj Aug 14, 2021
048fedc
Addressing major feedback items
Turnerj Aug 17, 2021
2e75bb3
Removed unnecessary null-checks
Turnerj Aug 17, 2021
d2dffea
Use VS 2022 for solution version
Turnerj Aug 24, 2021
1ec52c1
Use RC1 version of System.Text.Json
Turnerj Sep 20, 2021
df836cb
Include prereleases for SDK installs
Turnerj Sep 20, 2021
d7ffe22
Fix compilation error with null checks
Turnerj Nov 9, 2021
47a3727
Update S.T.J to final v6.0.0
Turnerj Nov 9, 2021
4743388
Fix constant usage
Turnerj Nov 9, 2021
017d2cb
Remove pre-release requirement for build
Turnerj Nov 9, 2021
e7d3ae8
Minor bugfix for enum parsing
Turnerj Nov 9, 2021
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
Prev Previous commit
Next Next commit
Support EnumMember for schema enum serialization
  • Loading branch information
Turnerj committed Nov 9, 2021
commit 8e2056405fad9e203757894862675342dae35415
5 changes: 3 additions & 2 deletions Schema.NET.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31423.177
# Visual Studio Version 16
VisualStudioVersion = 16.0.31515.178
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{719809C2-A551-4C4A-9EFD-B10FB5E35BC0}"
ProjectSection(SolutionItems) = preProject
Expand Down Expand Up @@ -106,6 +106,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{AF228B
Source\Common\JsonLdObject.cs = Source\Common\JsonLdObject.cs
Source\Common\OneOrMany{T}.cs = Source\Common\OneOrMany{T}.cs
Source\Common\PropertyValueSpecification.Partial.cs = Source\Common\PropertyValueSpecification.Partial.cs
Source\Common\SchemaEnumJsonConverter{T}.cs = Source\Common\SchemaEnumJsonConverter{T}.cs
Source\Common\SchemaSerializer.cs = Source\Common\SchemaSerializer.cs
Source\Common\Thing.Partial.cs = Source\Common\Thing.Partial.cs
Source\Common\TimeSpanToISO8601DurationValuesJsonConverter.cs = Source\Common\TimeSpanToISO8601DurationValuesJsonConverter.cs
Expand Down
50 changes: 50 additions & 0 deletions Source/Common/EnumHelper.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
namespace Schema.NET
{
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

/// <summary>
/// Helper for parsing strings into Enum values.
/// </summary>
internal static class EnumHelper
{
private const string HttpSchemaOrgUrl = "http://schema.org/";
private const int HttpSchemaOrgLength = 18; // equivalent to "http://schema.org/".Length
private const string HttpsSchemaOrgUrl = "https://schema.org/";
private const int HttpsSchemaOrgLength = 19; // equivalent to "https://schema.org/".Length

/// <summary>
/// Converts the string representation of the name or numeric value of one or more
/// enumerated constants to an equivalent enumerated object.
Expand Down Expand Up @@ -43,5 +49,49 @@ public static bool TryParse(
#pragma warning restore IDE0022 // Use expression body for methods
#endif
}

/// <summary>
/// Converts the Schema URI representation of the enum type to an equivalent enumerated object.
/// </summary>
/// <param name="enumType">The enum type to use for parsing.</param>
/// <param name="value">The string representation of the name or numeric value of one or more enumerated constants.</param>
/// <param name="result">When this method returns true, an object containing an enumeration constant representing the parsed value.</param>
/// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
public static bool TryParseEnumFromSchemaUri(
Type enumType,
#if NETCOREAPP3_1_OR_GREATER
[NotNullWhen(true)]
#endif
string? value,
out object? result)
{
string? enumString;
if (value is not null && value.StartsWith(HttpSchemaOrgUrl, StringComparison.OrdinalIgnoreCase))
{
#pragma warning disable IDE0057 // Use range operator. Need to multi-target.
enumString = value.Substring(HttpSchemaOrgLength);
#pragma warning restore IDE0057 // Use range operator. Need to multi-target.
}
else if (value is not null && value.StartsWith(HttpsSchemaOrgUrl, StringComparison.OrdinalIgnoreCase))
{
#pragma warning disable IDE0057 // Use range operator. Need to multi-target.
enumString = value.Substring(HttpsSchemaOrgLength);
#pragma warning restore IDE0057 // Use range operator. Need to multi-target.
}
else
{
enumString = value;
}

if (TryParse(enumType, enumString, out result))
{
return true;
}
else
{
Debug.WriteLine($"Unable to parse enumeration of type {enumType.FullName} with value {enumString}.");
return false;
}
}
}
}
61 changes: 61 additions & 0 deletions Source/Common/SchemaEnumJsonConverter{T}.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
namespace Schema.NET
{
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.Json;
using System.Text.Json.Serialization;

/// <summary>
/// Converts a Schema enumeration to and from JSON.
/// </summary>
/// <typeparam name="T">The enum type to convert</typeparam>
public class SchemaEnumJsonConverter<T> : JsonConverter<T>
where T : struct, Enum
{
private readonly Dictionary<T, string> valueNameMap = new();

/// <summary>
/// Initializes a new instance of the <see cref="SchemaEnumJsonConverter{T}"/> class.
/// </summary>
public SchemaEnumJsonConverter()
{
var enumType = typeof(T);
var values = Enum.GetValues(enumType);

foreach (var value in values)
{
var enumMember = enumType.GetMember(value!.ToString()!)[0];
var enumMemberAttribute = enumMember.GetCustomAttribute<EnumMemberAttribute>(false);
this.valueNameMap[(T)value] = enumMemberAttribute!.Value!;
}
}

/// <summary>
/// Reads the JSON representation of the enumeration.
/// </summary>
/// <param name="reader">The <see cref="Utf8JsonReader"/> to read from.</param>
/// <param name="typeToConvert">Type of the enumeration.</param>
/// <param name="options">The serializer options.</param>
/// <returns>The enumeration value.</returns>
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var valueString = reader.GetString();
if (EnumHelper.TryParseEnumFromSchemaUri(typeToConvert, valueString, out var result))
{
return (T)(result!);
}

return default;
}

/// <summary>
/// Writes the specified enumeration using the JSON writer.
/// </summary>
/// <param name="writer">The JSON writer.</param>
/// <param name="value">The enumeration value.</param>
/// <param name="options">The JSON serializer options.</param>
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => writer.WriteStringValue(this.valueNameMap[value]);
}
}
4 changes: 0 additions & 4 deletions Source/Common/SchemaSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,18 @@ public static class SchemaSerializer
static SchemaSerializer()
#pragma warning restore CA1810 // Initialize reference type static fields inline
{
var stringEnumConverter = new JsonStringEnumConverter();

DefaultSerializationSettings = new JsonSerializerOptions
{
AllowTrailingCommas = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
DefaultSerializationSettings.Converters.Add(stringEnumConverter);

HtmlEscapedSerializationSettings = new JsonSerializerOptions
{
AllowTrailingCommas = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
};
HtmlEscapedSerializationSettings.Converters.Add(stringEnumConverter);
}

/// <summary>
Expand Down
35 changes: 2 additions & 33 deletions Source/Common/ValuesJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ namespace Schema.NET
/// <seealso cref="JsonConverter" />
public class ValuesJsonConverter : JsonConverter<IValues>
{
private const string HttpSchemaOrgUrl = "http://schema.org/";
private const int HttpSchemaOrgLength = 18; // equivalent to "http://schema.org/".Length
private const string HttpsSchemaOrgUrl = "https://schema.org/";
private const int HttpsSchemaOrgLength = 19; // equivalent to "https://schema.org/".Length

private static readonly Dictionary<string, Type> BuiltInThingTypeLookup = new(StringComparer.Ordinal);

static ValuesJsonConverter()
Expand Down Expand Up @@ -299,35 +294,9 @@ private static bool TryProcessTokenAsType(ref Utf8JsonReader reader, Type target
success = decimal.TryParse(valueString, NumberStyles.Float, CultureInfo.InvariantCulture, out var localResult);
result = localResult;
}
else if (targetType.GetTypeInfo().IsEnum)
else if (targetType.IsEnum)
{
string? enumString;
if (valueString is not null && valueString.StartsWith(HttpSchemaOrgUrl, StringComparison.OrdinalIgnoreCase))
{
#pragma warning disable IDE0057 // Use range operator. Need to multi-target.
enumString = valueString.Substring(HttpSchemaOrgLength);
#pragma warning restore IDE0057 // Use range operator. Need to multi-target.
}
else if (valueString is not null && valueString.StartsWith(HttpsSchemaOrgUrl, StringComparison.OrdinalIgnoreCase))
{
#pragma warning disable IDE0057 // Use range operator. Need to multi-target.
enumString = valueString.Substring(HttpsSchemaOrgLength);
#pragma warning restore IDE0057 // Use range operator. Need to multi-target.
}
else
{
enumString = valueString;
}

if (EnumHelper.TryParse(targetType, enumString, out result))
{
success = true;
}
else
{
Debug.WriteLine($"Unable to parse enumeration of type {targetType.FullName} with value {enumString}.");
success = false;
}
success = EnumHelper.TryParseEnumFromSchemaUri(targetType, valueString, out result);
}
else if (targetType == typeof(DateTime))
{
Expand Down
2 changes: 1 addition & 1 deletion Tools/Schema.NET.Tool/SchemaSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ private static string RenderEnumeration(GeneratorSchemaEnumeration schemaEnumera
/// <summary>
/// {SourceUtility.RenderDoc(4, schemaEnumeration.Description)}
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
[JsonConverter(typeof(SchemaEnumJsonConverter<{schemaEnumeration.Name}>))]
public enum {schemaEnumeration.Name}
{{{SourceUtility.RenderItems(schemaEnumeration.Values, value => $@"
/// <summary>
Expand Down