Updated Implementation Using System.Text.Json

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

public abstract class MessageEnvelope<T>
{
    // Properties remain immutable
    public string EventType { get; private init; }
    public string SourceService { get; private init; }
    public DateTime Timestamp { get; private init; }
    public Guid TraceId { get; private init; }
    public T Payload { get; private init; }

    // Private constructor for builder use only
    private MessageEnvelope() { }

    public static Builder CreateBuilder() => new Builder();

    // Serialization to JSON
    public string ToJson()
    {
        var options = new JsonSerializerOptions
        {
            WriteIndented = true // Makes the output JSON easier to read
        };
        return JsonSerializer.Serialize(this, options);
    }

    // Deserialization from JSON
    public static MessageEnvelope<T> FromJson(string json)
    {
        var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true // Handles case differences in JSON properties
        };
        return JsonSerializer.Deserialize<ConcreteMessageEnvelope>(json, options);
    }

    // Concrete implementation example for proper deserialization
    private class ConcreteMessageEnvelope : MessageEnvelope<T> { }

    public class Builder
    {
        private readonly MessageEnvelope<T> _envelope = new ConcreteMessageEnvelope();

        public Builder WithEventType(string eventType)
        {
            _envelope.EventType = eventType;
            return this;
        }

        public Builder WithSourceService(string sourceService)
        {
            _envelope.SourceService = sourceService;
            return this;
        }

        public Builder WithPayload(T payload)
        {
            _envelope.Payload = payload;
            return this;
        }

        public Builder WithTimestamp(DateTime timestamp)
        {
            _envelope.Timestamp = timestamp;
            return this;
        }

        public Builder WithTraceId(Guid traceId)
        {
            _envelope.TraceId = traceId;
            return this;
        }

        public MessageEnvelope<T> Build()
        {
            _envelope.Timestamp = _envelope.Timestamp == default ? DateTime.UtcNow : _envelope.Timestamp;
            _envelope.TraceId = _envelope.TraceId == default ? Guid.NewGuid() : _envelope.TraceId;
            return _envelope;
        }
    }
}

Example Usage

1. Serialize a MessageEnvelope to JSON

var envelope = MessageEnvelope<Reservation>
    .CreateBuilder()
    .WithEventType("ReservationExpiry")
    .WithSourceService("ReservationService")
    .WithPayload(new Reservation
    {
        ReservationId = "res-001",
        SlotId = "slot-123",
        ExpiryTime = DateTime.UtcNow
    })
    .Build();

string serialized = envelope.ToJson();
Console.WriteLine($"Serialized Envelope:\n{serialized}");

2. Deserialize a JSON String to MessageEnvelope

string json = "{\"EventType\":\"ReservationExpiry\",\"SourceService\":\"ReservationService\",\"Timestamp\":\"2025-03-25T17:00:00Z\",\"TraceId\":\"8a1db2c2-ec3e-45f7-a3eb-bd9dfb351245\",\"Payload\":{\"ReservationId\":\"res-001\",\"SlotId\":\"slot-123\",\"ExpiryTime\":\"2025-03-25T17:00:00Z\"}}";

var deserializedEnvelope = MessageEnvelope<Reservation>.FromJson(json);

Console.WriteLine($"Deserialized EventType: {deserializedEnvelope.EventType}");
Console.WriteLine($"Deserialized Reservation ID: {deserializedEnvelope.Payload.ReservationId}");

Benefits of Serialization

  • Portability: You can transmit envelopes as JSON over APIs, message brokers, or store them in databases.
  • Interoperability: Many systems can parse JSON, making serialized envelopes easy to integrate across platforms.
  • Flexibility: Deserialization lets you reconstruct envelopes when receiving messages.

Advantages of System.Text.Json

  1. Performance: Faster than Newtonsoft.Json, especially for large-scale applications.
  2. Built-in Support: No need for external dependencies; it’s natively part of .NET Core and .NET 5+.
  3. Configuration Options: Flexible JSON options like camel casing, case insensitivity, and indented formatting.

Related Posts