LukeP
LukeP

Reputation: 1605

Serialize struct/class as a single string

I have a struct that I use in my ViewModels in order to simplify the way they get automatically serialized. I want to the serialization of it to simply call the .ToString() method. Currently when it's serialized to JSON it gets turned into: "{}". Here is my Date class:

public struct Date
{
    private DateTime _date;

    public Date(DateTime date)
    {
        _date = date;
    }

    public static implicit operator Date(DateTime date) => new Date(date);

    public override string ToString() => _date.ToString("yyyy-MM-dd");
}

I thought that there might be some sort of attribute that I can decorate the struct with or perhaps some interface that I can implement but it doesn't appear to help.

Upvotes: 1

Views: 1189

Answers (2)

Abhinav Galodha
Abhinav Galodha

Reputation: 9908

A Lot of times, this is required in a lot of Classes when the project is bigger.

We need the conversion to work both ways. A class should be serialized using the ToString() method, and the class should be deserialized from the String. We use the following convention.

Define marker interfaces to let classes explicitly adhere to the contract that they support Serialization using the ToString method and also support Deserialization from string to object instance.

    /// <summary>
    /// Interface to convert string to a type T
    /// </summary>
    public interface IStringToTypeConverter<out T>
    {
        T ConvertToType(string stringToConvertFrom);

    }

    /// <summary>
    /// Marker Interface to let Serialization/Deserialization work on the ToString Method of the class, Rather than
    /// calling on the Instance properties
    /// </summary>
    public interface ITypeToStringConverter
    {
    }

Next, Define the generic converter which will do the conversion (Serialization/Deserialization) for a class which implements the above interfaces.

    public class ToStringJsonConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {

            var isTypeImplementStringToTypeConverter = objectType.GetInterfaces().Any(x =>
                x == typeof(ITypeToStringConverter) ||
                (x.IsGenericType &&
                x.GetGenericTypeDefinition() == typeof(IStringToTypeConverter<>)));

            return isTypeImplementStringToTypeConverter;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteValue(value.ToString());
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // Load the JSON for the Result into a JObject

            var stringValue = reader.Value.ToString();

            if (string.IsNullOrWhiteSpace(stringValue))
            {
                var jObject = JObject.Load(reader);
                stringValue = jObject.ToString();

            }

            MethodInfo parse = objectType.GetMethod("ConvertToType");
            if (parse != null)
            {
                var destinationObject = Activator.CreateInstance(objectType,stringValue);
                return parse.Invoke(destinationObject, new object[] { stringValue });
            }

            throw new JsonException($"The {objectType.Name} type does not have a public ConvertToType(string) method.");
        }
    }

Lastly, Add the Converter to the startup class, passing it in the JSON Options

services.AddMvc().AddJsonOptions(options =>
                {
                    options.SerializerSettings.Converters.Add(new ToStringJsonConverter());

                })

Note: Please test the performance benhmarks for your code to see if it has any impact on your performance SLA.

Upvotes: 1

LukeP
LukeP

Reputation: 1605

After some further research it looks like MVC uses the JsonConverter attribute to serialize JsonResults. The code below accomplished what I was trying to do.

[JsonConverter(typeof(DateToString))]
public struct Date
{
    private DateTime _date;

    public Date(DateTime date)
    {
        _date = date;
    }

    public static implicit operator Date(DateTime date) => new Date(date);

    public override string ToString() => _date.ToString("yyyy-MM-dd");
}

public class DateToString : JsonConverter
{
    public override bool CanConvert(Type objectType) => objectType == typeof(Date);
    public override bool CanRead => false;
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        => throw new NotImplementedException();

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) =>
        writer.WriteValue(value.ToString());
}

Upvotes: 0

Related Questions