Search code examples
c#jsonservicestackservicestack-text

Custom Serialization using Attributes and ServiceStack.Text.JsonSerializer


We use custom attributes to annotate data how it should be displayed:

public class DcStatus
{
    [Format("{0:0.0} V")]   public Double Voltage { get; set; }
    [Format("{0:0.000} A")] public Double Current { get; set; }
    [Format("{0:0} W")]     public Double Power => Voltage * Current;
}

The properties are processed with String.Format using the format provided by the attribute.

How do we need to configure ServiceStack.Text.JsonSerializer to use that attribute too?

Example:

var test = new DcStatus {Voltage = 10, Current = 1.2};
var json = JsonSerializer.SerializeToString(test);

should produce

{
    "Voltage": "10.0 V",
    "Current": "1.200 A",
    "Power"  : "12 W",
}

Solution

  • There's no customizable callback that lets you modify how built in types are serialized based on a custom property attribute.

    One option to get the desired Custom Serialization for this Type is to implement a ToJson() method which ServiceStack.Text uses instead, e.g:

    public class DcStatus
    {
        [Format("{0:0.0} V")]
        public Double Voltage { get; set; }
        [Format("{0:0.000} A")]
        public Double Current { get; set; }
        [Format("{0:0} W")]
        public Double Power => Voltage * Current;
    
        public string ToJson()
        {
            return new Dictionary<string,string>
            {
                { "Voltage", "{0:0.0} V".Fmt(Voltage) },
                { "Current", "{0:0.000} A".Fmt(Current) },
                { "Power", "{0:0} W".Fmt(Power) },
            }.ToJson();
        }
    }
    

    Which prints out the desired result:

    var test = new DcStatus { Voltage = 10, Current = 1.2 };
    test.ToJson().Print(); //= {"Voltage":"10.0 V","Current":"1.200 A","Power":"12 W"}
    

    Otherwise if you don't want to change the existing Type you can also customize the serialization for an existing type by registering a JsConfig<T>.RawSerializeFn impl and returning the custom JSON you want to use instead, e.g:

     JsConfig<DcStatus2>.RawSerializeFn = o => new Dictionary<string, string> {
        { "Voltage", "{0:0.0} V".Fmt(o.Voltage) },
        { "Current", "{0:0.000} A".Fmt(o.Current) },
        { "Power", "{0:0} W".Fmt(o.Power) },
    }.ToJson();