일반 JsonConverter를 사용하는 C #의 JSON 직렬화 가능한 문자열 열거 형

Aug 17 2020

StringEnumeration아래 코드에서 제네릭 클래스로 변경하려면 어떻게 하면 클래스의 인스턴스가 연결된 문자열 값이있는 직렬화 가능한 열거 형인 것처럼 사용할 수 있도록 속성이 ConsensusState필요하지 않습니다 JsonConverter.

내가 사용하고 StringEnumeration인스턴스를 점검 할 수 있도록 ConsensusState전년도 string조건문 개체를 또는 콘솔에 직접 인쇄 string도에서 속성으로 사용할 수 있지만, 값 Serializable클래스. 직렬화는 System.Text.Json.Serialization.

수업은 다음과 같습니다.

/// <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();
    }
}

답변

1 rraallvv Aug 19 2020 at 05:01

StringEnumeration아래 의 구현은 서브 클래스가 마치 문자열 열거 형인 것처럼 동작하도록 허용하고 속성을 사용하여 각 필드에 대한 문자열 관련 값을 지정할 수있는 것과 같은 삶의 질을 향상시킵니다 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();
    }
}

사용 방법은 다음과 같습니다.

/// <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;
}