Chuỗi enum có thể tuần tự hóa JSON trong C # với JsonConverter chung

Aug 17 2020

Làm thế nào để có StringEnumerationthể thay đổi mã bên dưới thành một lớp chung, để ConsensusStatekhông cần JsonConverterthuộc tính để cho phép các thể hiện của lớp được sử dụng như thể chúng là các enum có thể tuần tự hóa với các giá trị chuỗi được liên kết?

Tôi đang sử dụng StringEnumerationđể cho phép kiểm tra các trường hợp của đối tượng ConsensusStateagains stringtrong các câu lệnh điều kiện hoặc in chúng trực tiếp ra bảng điều khiển dưới dạng stringgiá trị, đồng thời có thể sử dụng chúng làm thuộc tính trong Serializablecác lớp. Việc tuần tự hóa được thực hiện bằng cách sử dụng System.Text.Json.Serialization.

Đây là các lớp:

/// <summary>Consensus state returned by the server.</summary>
[Serializable]
[JsonConverter(typeof(StringEnumerationConverter<ConsensusState>))]
public class ConsensusState : StringEnumeration
{
    /// <summary>Connecting.</summary>
    public static readonly ConsensusState Connecting = new ConsensusState("connecting");
    /// <summary>Syncing blocks.</summary>
    public static readonly ConsensusState Syncing = new ConsensusState("syncing");
    /// <summary>Consensus established.</summary>
    public static readonly ConsensusState Established = new ConsensusState("established");

    private ConsensusState(string value) : base(value) { }
}

/// <summary>JsonConverter used in string enumeration serialization.</summary>
public class StringEnumerationConverter<T> : JsonConverter<T>
{
    /// <summary>Read the string value.</summary>
    /// <param name="reader">Reader to access the encoded JSON text.</param>
    /// <param name="typeToConvert">Type of the object to deserialize.</param>
    /// <param name="options">Options for the deserialization.</param>
    /// <returns>Underlying string enumeration type.</returns>
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return (T)Activator.CreateInstance(typeof(T), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { reader.GetString() }, null, null);
    }

    /// <summary>Write the string value.</summary>
    /// <param name="writer">Writer to encode the JSON text.</param>
    /// <param name="value">Object to serialize.</param>
    /// <param name="options">Options for the serialization.</param>
    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        writer.WriteStringValue((string)typeof(T).GetProperty("Value").GetValue(value, null));
    }
}

/// <summary>Abstract base class used in string enumerations.</summary>
public abstract class StringEnumeration
{
    /// <summary>Associated value.</summary>
    public string Value { get; set; }

    /// <summary>Initializes the enumeration from a string.</summary>
    /// <param name="value">The associated value.</param>
    public StringEnumeration(string value) { Value = value; }

    /// <summary>Implicit conversion to string.</summary>
    /// <param name="obj">And StringEnumeration object.</param>
    /// <returns>string object.</returns>
    public static implicit operator string(StringEnumeration obj)
    {
        return obj.Value;
    }

    /// <summary>Get the string associated value.</summary>
    /// <returns>string object.</returns>
    public override string ToString()
    {
        return Value;
    }

    /// <summary>Test whether a StringEnumeration is equal to another object.</summary>
    /// <param name="a">StringEnumeration object.</param>
    /// <param name="b">Another object.</param>
    /// <returns>true if the two objects are equal.</returns>
    public static bool operator ==(StringEnumeration a, object b)
    {
        var other = b as StringEnumeration;
        if (other is null)
        {
            return false;
        }
        return a.Value == other.Value;
    }

    /// <summary>Test whether a StringEnumeration is different to other object.</summary>
    /// <param name="a">StringEnumeration object.</param>
    /// <param name="b">Another object.</param>
    /// <returns>true if the two objects are different.</returns>
    public static bool operator !=(StringEnumeration a, object b)
    {
        var other = b as StringEnumeration;
        if (other is null)
        {
            return true;
        }
        return a.Value != other.Value;
    }

    /// <summary>Test whether a StringEnumeration is equal to another object.</summary>
    /// <param name="obj">Another object.</param>
    /// <returns>true if the objects are equal.</returns>
    public override bool Equals(object obj)
    {
        var other = obj as StringEnumeration;
        if (other is null)
        {
            return false;
        }
        return Value == other.Value;
    }

    /// <summary>Get the hash code of the associated value.</summary>
    /// <returns>An integer value representing the hash of the associated value.</returns>
    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }
}

Trả lời

1 rraallvv Aug 19 2020 at 05:01

Việc triển khai StringEnumerationbên dưới cho phép một lớp con hoạt động như thể nó là một chuỗi ký tự và cũng có một số cải tiến về chất lượng cuộc sống như có thể chỉ định giá trị liên kết chuỗi cho mỗi trường bằng cách sử dụng thuộc tính JsonStringValue().

/// <summary>Attribute to assign the string value of fields in StringEnumeration.</summary>
[AttributeUsage(AttributeTargets.Field)]
public class JsonStringValue : Attribute
{
    /// <summary>String value.</summary>
    public string Value;

    /// <summary>Initializes the Attribute instance to a given value.</summary>
    public JsonStringValue(string value)
    {
        Value = value;
    }
}

/// <summary>JsonConverter used in string enumeration serialization.</summary>
public class StringEnumerationConverter : JsonConverter<StringEnumeration>
{
    /// <summary>Whether a type is a subclass of <c>StringEnumeration</c>.</summary>
    /// <param name="typeToConvert">Type to check.</param>
    /// <returns>True if is a subclass.</returns>
    public override bool CanConvert(Type typeToConvert)
    {
        return typeof(StringEnumeration).IsAssignableFrom(typeToConvert);
    }

    /// <summary>Read the string value.</summary>
    /// <param name="reader">Reader to access the encoded JSON text.</param>
    /// <param name="typeToConvert">Type of the object to deserialize.</param>
    /// <param name="options">Options for the deserialization.</param>
    /// <returns>Underlying string enumeration type.</returns>
    public override StringEnumeration Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var obj = (StringEnumeration)FormatterServices.GetUninitializedObject(typeToConvert);
        obj.Value = reader.GetString();
        return obj;
    }

    /// <summary>Write the string value.</summary>
    /// <param name="writer">Writer to encode the JSON text.</param>
    /// <param name="value">Object to serialize.</param>
    /// <param name="options">Options for the serialization.</param>
    public override void Write(Utf8JsonWriter writer, StringEnumeration value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value);
    }
}

/// <summary>Abstract base class used in string enumerations.</summary>
public abstract class StringEnumeration
{
    /// <summary>Associated value.</summary>
    public string Value { get; set; }

    /// <summary>Initializes all static fields in subclasses.</summary>
    static StringEnumeration()
    {
        var types = typeof(StringEnumeration).Assembly.GetTypes();
        foreach (var type in types)
        {
            if (type.BaseType == typeof(StringEnumeration))
            {
                var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static);
                foreach (var field in fields)
                {
                    var attribute = field.GetCustomAttribute<JsonStringValue>();
                    var name = attribute is null ? field.Name : attribute.Value;
                    var value = (StringEnumeration)FormatterServices.GetUninitializedObject(type);
                    value.Value = name;
                    field.SetValue(null, value);
                }
            }
        }
    }

    /// <summary>Implicit conversion to string.</summary>
    /// <param name="obj">And StringEnumeration object.</param>
    /// <returns>string object.</returns>
    public static implicit operator string(StringEnumeration obj)
    {
        if (obj is null)
        {
            return null;
        }
        return obj.ToString();
    }

    /// <summary>Get the string associated value.</summary>
    /// <returns>string object.</returns>
    public override string ToString()
    {
        return Value;
    }

    /// <summary>Test whether a StringEnumeration is equal to another object.</summary>
    /// <param name="a">StringEnumeration object.</param>
    /// <param name="b">Another object.</param>
    /// <returns>true if the two objects are equal.</returns>
    public static bool operator ==(StringEnumeration a, object b)
    {
        if (a is null)
        {
            return b is null;
        }
        return !(b is null) && a.Value == b.ToString();
    }

    /// <summary>Test whether a StringEnumeration is different to other object.</summary>
    /// <param name="a">StringEnumeration object.</param>
    /// <param name="b">Another object.</param>
    /// <returns>true if the two objects are different.</returns>
    public static bool operator !=(StringEnumeration a, object b)
    {
        return !(a == b);
    }

    /// <summary>Test whether a StringEnumeration is equal to another object.</summary>
    /// <param name="obj">Another object.</param>
    /// <returns>true if the objects are equal.</returns>
    public override bool Equals(object obj)
    {
        return this == obj;
    }

    /// <summary>Get the hash code of the associated value.</summary>
    /// <returns>An integer value representing the hash of the associated value.</returns>
    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }
}

Đây là cách sử dụng nó:

/// <summary>Consensus state returned by the server.</summary>
[Serializable]
[JsonConverter(typeof(StringEnumerationConverter))]
public class ConsensusState : StringEnumeration
{
    /// <summary>Connecting.</summary>
    [JsonStringValue("connecting")]
    public static ConsensusState Connecting;
    /// <summary>Syncing blocks.</summary>
    [JsonStringValue("syncing")]
    public static ConsensusState Syncing;
    /// <summary>Consensus established.</summary>
    [JsonStringValue("established")]
    public static ConsensusState Established;
}