Skip to content

Commit

Permalink
Update JSON converter.
Browse files Browse the repository at this point in the history
  • Loading branch information
kingcean committed Jan 10, 2025
1 parent 8fe1b08 commit 8ef78b8
Show file tree
Hide file tree
Showing 13 changed files with 717 additions and 612 deletions.
21 changes: 21 additions & 0 deletions Core/Maths/Boolean/Operations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,27 @@ public static string ToString(CriteriaBooleanOperator op)
public static string ToString(IEnumerable<BooleanBinaryOperationFormula> col)
=> string.Join(Environment.NewLine, col.Select(ele => ele?.ToString()));

/// <summary>
/// Converts a boolean to string format customized.
/// </summary>
/// <param name="value">A boolean value.</param>
/// <param name="trueString">true in string.</param>
/// <param name="falseString">false in string.</param>
/// <returns>The customized string converted from boolean.</returns>
public static string ToString(bool value, string trueString, string falseString)
=> value ? trueString : falseString;

/// <summary>
/// Converts a boolean to string format customized.
/// </summary>
/// <param name="value">A boolean value.</param>
/// <param name="trueString">true in string.</param>
/// <param name="falseString">false in string.</param>
/// <param name="nullString">null in string.</param>
/// <returns>The customized string converted from boolean.</returns>
public static string ToString(bool? value, string trueString, string falseString, string nullString)
=> value.HasValue ? ToString(value.Value, trueString, falseString) : nullString;

/// <summary>
/// Calculates by boolean operation.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion Core/Reflection/Lifecycle/ObservableProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ protected string GetPropertyJson<T>(string key, JsonSerializerOptions options =
/// Writes this instance to the specified writer as a JSON value.
/// </summary>
/// <param name="writer">The writer to which to write this instance.</param>
protected void WriteTo(Utf8JsonWriter writer)
protected virtual void WriteTo(Utf8JsonWriter writer)
=> JsonObjectNode.ConvertFrom(this).WriteTo(writer);

/// <summary>
Expand Down
76 changes: 62 additions & 14 deletions Core/Text/JsonConverter/Object.cs
Original file line number Diff line number Diff line change
Expand Up @@ -398,13 +398,41 @@ public override void Write(Utf8JsonWriter writer, JsonNodeSchemaDescription valu
}
}

/// <summary>
/// JSON object node host converter.
/// </summary>
internal sealed class HostConverter<T> : JsonConverter<T> where T : IJsonObjectHost
{
/// <inheritdoc />
public override bool CanConvert(Type typeToConvert)
=> typeof(IJsonObjectHost).IsAssignableFrom(typeToConvert);

/// <inheritdoc />
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> ReadHost<T>(ref reader, typeToConvert, options);

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
var json = value?.ToJson();
if (json is null) writer.WriteNullValue();
json.WriteTo(writer);
}
}

/// <inheritdoc />
public override bool CanConvert(Type typeToConvert)
=> typeof(IJsonValueNode).IsAssignableFrom(typeToConvert) || typeToConvert == typeof(JsonNodeSchemaDescription);
=> typeof(IJsonValueNode).IsAssignableFrom(typeToConvert) || typeof(IJsonObjectHost).IsAssignableFrom(typeToConvert) || typeToConvert == typeof(JsonNodeSchemaDescription);

/// <inheritdoc />
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
if (typeof(IJsonObjectHost).IsAssignableFrom(typeToConvert))
{
var type = typeof(HostConverter<>).MakeGenericType(new[] { typeToConvert });
return (JsonConverter)Activator.CreateInstance(type);
}

if (typeToConvert == typeof(IJsonValueNode)) return new CommonConverter();
if (typeToConvert == typeof(JsonObjectNode)) return new ObjectConverter();
if (typeToConvert == typeof(JsonArrayNode)) return new ArrayConverter();
Expand Down Expand Up @@ -446,46 +474,66 @@ internal static void Write(Utf8JsonWriter writer, IJsonValueNode value, JsonSeri
else if (value is IJsonValueNode<decimal> jDecimal) writer.WriteNumberValue(jDecimal.Value);
else writer.WriteNullValue();
}

internal static T ReadHost<T>(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
where T : IJsonObjectHost
{
JsonValues.SkipComments(ref reader);
switch (reader.TokenType)
{
case JsonTokenType.Null:
return default;
case JsonTokenType.StartObject:
if (typeToConvert.IsInterface) throw new NotSupportedException();
var constructor = typeToConvert.GetConstructor(new[] { typeof(JsonObjectNode), typeof(JsonSerializerOptions) });
if (constructor != null) return (T)constructor.Invoke(new object[] { new JsonObjectNode(ref reader), options });
constructor = typeToConvert.GetConstructor(new[] { typeof(JsonObjectNode) });
if (constructor != null) return (T)constructor.Invoke(new[] { new JsonObjectNode(ref reader) });
throw new JsonException("Requires a constructor with an argument of which type is JsonObjectNode.", new InvalidOperationException("Cannot deserialize the entity."));
default:
throw new JsonException($"The token type is {reader.TokenType} but expect JSON object or null for {typeToConvert}.");
}
}
}

#if NET7_0_OR_GREATER || NET462_OR_GREATER
/// <summary>
/// JSON object node host converter.
/// </summary>
public sealed class JsonObjectHostConverter : JsonConverter<IJsonObjectHost>
/// <typeparam name="T">The type of the model.</typeparam>
public abstract class JsonObjectHostConverter<T> : JsonConverter<T> where T : IJsonObjectHost
{
private readonly static JsonConverter<IJsonObjectHost> defaultConverter = (JsonConverter<IJsonObjectHost>)JsonSerializerOptions.Default.GetConverter(typeof(IJsonObjectHost));

/// <inheritdoc />
public override bool CanConvert(Type typeToConvert)
=> typeof(IJsonObjectHost).IsAssignableFrom(typeToConvert);

/// <inheritdoc />
public override IJsonObjectHost Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
JsonValues.SkipComments(ref reader);
switch (reader.TokenType)
{
case JsonTokenType.Null:
return default;
case JsonTokenType.StartObject:
if (typeToConvert.IsInterface) throw new NotSupportedException();
var constructor = typeToConvert.GetConstructor(new[] { typeof(JsonObjectNode), typeof(JsonSerializerOptions) });
if (constructor != null) return (IJsonObjectHost)constructor.Invoke(new object[] { new JsonObjectNode(ref reader), options });
constructor = typeToConvert.GetConstructor(new[] { typeof(JsonObjectNode) });
if (constructor != null) return (IJsonObjectHost)constructor.Invoke(new[] { new JsonObjectNode(ref reader) });
return defaultConverter.Read(ref reader, typeToConvert, options);
var json = JsonObjectNode.ParseValue(ref reader);
return json is null ? default : Create(json);
default:
throw new JsonException($"The token type is {reader.TokenType} but expect JSON object or null for {typeToConvert}.");
}
}

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, IJsonObjectHost value, JsonSerializerOptions options)
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
var json = value?.ToJson();
if (json is null) writer.WriteNullValue();
json.WriteTo(writer);
}

/// <summary>
/// Creates the instance by JSON object.
/// </summary>
/// <param name="json">The JSON object.</param>
/// <returns>The instance.</returns>
protected abstract T Create(JsonObjectNode json);
}
#endif
61 changes: 55 additions & 6 deletions Core/Text/JsonNode/Values.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using System.Runtime.Serialization.Json;
using System.Net.Http;
using Trivial.IO;
using Trivial.Maths;

namespace Trivial.Text;

Expand Down Expand Up @@ -1405,6 +1406,14 @@ public static string ToString(IJsonObjectHost value, IndentStyles indent = Inden
return json.ToString(indent);
}

/// <summary>
/// Converts to string format.
/// </summary>
/// <param name="value">A boolean value.</param>
/// <returns>The string converted in JSON format.</returns>
public static string ToString(bool? value)
=> BooleanOperations.ToString(value, JsonBooleanNode.TrueString, JsonBooleanNode.FalseString, NullString);

/// <summary>
/// Converts to a specific type.
/// </summary>
Expand Down Expand Up @@ -2415,7 +2424,7 @@ internal static JsonObjectNode ToJson(Dictionary<string, JsonNodeSchemaDescripti
}

internal static double ToDouble(int? value)
=> value.HasValue ? value.Value : double.NaN;
=> value ?? double.NaN;

internal static JsonNodeSchemaDescription CreateEnumSchema(Type type, JsonNodeSchemaDescription result = null)
{
Expand All @@ -2435,7 +2444,6 @@ internal static JsonNodeSchemaDescription CreateEnumSchema(Type type, JsonNodeSc
return desc;
}


internal static void RemoveJsonNodeSchemaDescriptionExtendedProperties(JsonObjectNode json, bool onlyBase)
{
json.SetRange(json);
Expand All @@ -2456,13 +2464,51 @@ public static string ToTypeScriptDefinitionString(this JsonObjectSchemaDescripti
{
if (source == null) return string.IsNullOrWhiteSpace(name) ? string.Empty : string.Concat(name, ": null | undefined;");
var sb = new StringBuilder();
ToTypeScriptDefinitionString("export interface ", name, source.Description, sb, 0);
ToTypeScriptDefinitionString("declare interface ", name, source.Description, sb, 0);
if (!string.IsNullOrWhiteSpace(name)) sb.Append(' ');
ToTypeScriptDefinitionString(source, sb, 0);
sb.AppendLine();
return sb.ToString();
}

/// <summary>
/// Converts the JSON operation to Type Script definition (format of .d.ts file) content string.
/// </summary>
/// <param name="source">The JSON operation to convert.</param>
/// <param name="name">The function name.</param>
/// <returns>A Type Script definition format content string.</returns>
/// <exception cref="ArgumentException">name was empty or consists only of white-space characters.</exception>
/// <exception cref="ArgumentNullException">name was null.</exception>
public static string ToTypeScriptDefinitionString(this JsonOperationDescription source, string name)
{
StringExtensions.AssertNotWhiteSpace(nameof(name), name);
if (source == null) return string.Concat("declare function ", name, "(): void", Environment.NewLine);
var sb = new StringBuilder();
ToTypeScriptDefinitionString("declare function ", name, source.Description, sb, 0);
if (source.ArgumentSchema == null)
{
sb.Append("(): ");
}
else
{
sb.Append("(args: ");
ToTypeScriptDefinitionString(source.ArgumentSchema, sb, 0);
sb.Append("): ");
}

if (source.ResultSchema == null)
{
sb.Append("void;");
}
else
{
ToTypeScriptDefinitionString(source.ResultSchema, sb, 0);
sb.AppendLine(";");
}

return sb.ToString();
}

/// <summary>
/// Converts the JSON schema to Type Script definition (format of .d.ts file) content string.
/// </summary>
Expand All @@ -2477,8 +2523,8 @@ private static void ToTypeScriptDefinitionString(JsonNodeSchemaDescription sourc
sb.Append(str.ConstantValue ?? "string");
else if (source is JsonNumberSchemaDescription || source is JsonIntegerSchemaDescription)
sb.Append("number");
else if (source is JsonBooleanSchemaDescription)
sb.Append("boolean");
else if (source is JsonBooleanSchemaDescription b)
sb.Append(BooleanOperations.ToString(b.ConstantValue, JsonBooleanNode.TrueString, JsonBooleanNode.FalseString, "boolean"));
else if (source is JsonObjectSchemaDescription obj)
ToTypeScriptDefinitionString(obj, sb, indent);
else if (source is JsonArraySchemaDescription arr)
Expand Down Expand Up @@ -2508,12 +2554,15 @@ private static void ToTypeScriptDefinitionString(JsonObjectSchemaDescription sou
var indentStr = new string(' ', indent * 2);
sb.AppendLine("{");
indent++;
var hasDesc = false;
foreach (var prop in source.Properties)
{
var key = prop.Key;
var v = prop.Value;
if (string.IsNullOrWhiteSpace(key) || v == null) continue;
if (hasDesc) sb.AppendLine();
ToTypeScriptDefinitionString(null, key, v.Description, sb, indent);
if (!string.IsNullOrWhiteSpace(v.Description)) hasDesc = true;
if (!source.RequiredPropertyNames.Contains(key)) sb.Append('?');
sb.Append(": ");
ToTypeScriptDefinitionString(v, sb, indent);
Expand All @@ -2522,6 +2571,7 @@ private static void ToTypeScriptDefinitionString(JsonObjectSchemaDescription sou

if (!source.DisableAdditionalProperties || (source.PatternProperties != null && source.PatternProperties.Count > 0))
{
if (hasDesc) sb.AppendLine();
sb.Append(indentStr);
sb.AppendLine(" [key: string]: any;");
}
Expand Down Expand Up @@ -2557,7 +2607,6 @@ private static void ToTypeScriptDefinitionString(string prefix, string name, str
var indentStr = new string(' ', indent * 2);
if (!string.IsNullOrWhiteSpace(description))
{
sb.AppendLine();
sb.Append(indentStr);
sb.AppendLine("/** ");
sb.Append(indentStr);
Expand Down
Loading

0 comments on commit 8ef78b8

Please sign in to comment.