docs consolidation and others
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// HlcTimestampJsonConverter.cs
|
||||
// Sprint: SPRINT_20260105_002_001_LB_hlc_core_library
|
||||
// Task: HLC-006 - Add HlcTimestampJsonConverter for System.Text.Json serialization
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.HybridLogicalClock;
|
||||
|
||||
/// <summary>
|
||||
/// JSON converter for HlcTimestamp using the sortable string format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Serializes HlcTimestamp to/from the sortable string format (e.g., "1704067200000-scheduler-east-1-000042").
|
||||
/// This format is both human-readable and lexicographically sortable.
|
||||
/// </remarks>
|
||||
public sealed class HlcTimestampJsonConverter : JsonConverter<HlcTimestamp>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override HlcTimestamp Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Null)
|
||||
{
|
||||
throw new JsonException("Cannot convert null value to HlcTimestamp");
|
||||
}
|
||||
|
||||
if (reader.TokenType != JsonTokenType.String)
|
||||
{
|
||||
throw new JsonException($"Expected string but got {reader.TokenType}");
|
||||
}
|
||||
|
||||
var value = reader.GetString();
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new JsonException("Cannot convert empty string to HlcTimestamp");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return HlcTimestamp.Parse(value);
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
throw new JsonException($"Invalid HlcTimestamp format: {value}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, HlcTimestamp value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToSortableString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JSON converter for nullable HlcTimestamp.
|
||||
/// </summary>
|
||||
public sealed class NullableHlcTimestampJsonConverter : JsonConverter<HlcTimestamp?>
|
||||
{
|
||||
private readonly HlcTimestampJsonConverter _inner = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override HlcTimestamp? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _inner.Read(ref reader, typeof(HlcTimestamp), options);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, HlcTimestamp? value, JsonSerializerOptions options)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
writer.WriteNullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
_inner.Write(writer, value.Value, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JSON converter for HlcTimestamp using object format with individual properties.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alternative converter that serializes HlcTimestamp as an object:
|
||||
/// <code>
|
||||
/// {
|
||||
/// "physicalTime": 1704067200000,
|
||||
/// "nodeId": "scheduler-east-1",
|
||||
/// "logicalCounter": 42
|
||||
/// }
|
||||
/// </code>
|
||||
/// Use this when you need to query individual fields in JSON storage.
|
||||
/// </remarks>
|
||||
public sealed class HlcTimestampObjectJsonConverter : JsonConverter<HlcTimestamp>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override HlcTimestamp Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
{
|
||||
throw new JsonException($"Expected StartObject but got {reader.TokenType}");
|
||||
}
|
||||
|
||||
long? physicalTime = null;
|
||||
string? nodeId = null;
|
||||
int? logicalCounter = null;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.EndObject)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (reader.TokenType != JsonTokenType.PropertyName)
|
||||
{
|
||||
throw new JsonException($"Expected PropertyName but got {reader.TokenType}");
|
||||
}
|
||||
|
||||
var propertyName = reader.GetString();
|
||||
reader.Read();
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case "physicalTime":
|
||||
case "PhysicalTime":
|
||||
physicalTime = reader.GetInt64();
|
||||
break;
|
||||
case "nodeId":
|
||||
case "NodeId":
|
||||
nodeId = reader.GetString();
|
||||
break;
|
||||
case "logicalCounter":
|
||||
case "LogicalCounter":
|
||||
logicalCounter = reader.GetInt32();
|
||||
break;
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!physicalTime.HasValue)
|
||||
throw new JsonException("Missing required property 'physicalTime'");
|
||||
if (string.IsNullOrEmpty(nodeId))
|
||||
throw new JsonException("Missing required property 'nodeId'");
|
||||
if (!logicalCounter.HasValue)
|
||||
throw new JsonException("Missing required property 'logicalCounter'");
|
||||
|
||||
return new HlcTimestamp
|
||||
{
|
||||
PhysicalTime = physicalTime.Value,
|
||||
NodeId = nodeId,
|
||||
LogicalCounter = logicalCounter.Value
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, HlcTimestamp value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteNumber("physicalTime", value.PhysicalTime);
|
||||
writer.WriteString("nodeId", value.NodeId);
|
||||
writer.WriteNumber("logicalCounter", value.LogicalCounter);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user