Transforming rendering of DTOs before returning

Firstly, thank you for the excellent ServiceStack libraries.

I have a DTO which contains a Byte array. When this is returned it is rendered as a Base64 string in JSON.

The consumers of my service would rather consume this value as a Hex string.

I already have the code to convert my byte[] into string (Hex), but I cannot find a location to hook into the rendering of this.

Can anyone point me in the right direction to intercept the serialization of members of a DTO?

I would like to intercept the serialization, check for an attribute on a member, and if so serialize to Hex rather than Base64.

Thank you!

This should do it :slight_smile:

JsConfig<byte[]>.SerializeFn = BitConverter.ToString;
JsConfig<byte[]>.DeSerializeFn = str =>
{
    var hex = str.Replace("-", "");
    return Enumerable.Range(0, hex.Length)
        .Where(x => x%2 == 0)
        .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
        .ToArray();
};

Note: when customizing the Serializer this needs to be defined in both client/server.

Perfect, I will look into this, thank you very much.

Hello mythz,

This works very well for overriding the serialization of byte arrays, and is fine for now, however I would like the ability to specify certain properties to be serialized to hex whilst others are their default Base64.

Is there a function in which the DTO Property is passed to SerializeFn which I can use to check for an attribute on serialization?

Many thanks,
James

No there isn’t scoped support for custom serializers to only applies to selective properties.

If you only wanted to serialize bytes as Hex you can ignore the byte[] property and have a computed property serializing how you want, e.g:

 [IgnoreDataMember]
 public byte[] Bytes { get; set; }

 public string HexBytes 
 { 
     get { return BitConverter.ToString(Bytes); }
 }

Otherwise just have a string on the DTO to serialize how you want it, e.g:

return new Response {
    HexBytes = BitConverter.ToString(bytes),
};

Hi mythz,

Thank you for clarifying, I will look into other ways to achieve this.

Kind regards,
James

===== EDIT =====

So the caching was the issue, by doing the following:

        JsConfig<byte[]>.SerializeFn = Encoders.Hex.GetString;
        JsConfig<byte[]>.DeSerializeFn = Encoders.Hex.GetBytes;

        var appHost = new SelfHostingServices(container);

It sets up the JsConfig before the AppHost is instantiated and caches the Request deserializers.

You can ignore my request :slight_smile: Working fine now!

==============
Hi mythz,

So your method does work for Serializing DTO’s, however I’m having a problem with the following route:

[Route("/test/{Bytes}")]
public class TestRequest
{
    public byte[] Bytes { get; set; }
}

When I call this with a GET request, using Base64 it works fine, but when I call it with Hex it does not call the custom function.

However, if you do the following:

        JsConfig<byte[]>.SerializeFn = Encoders.Hex.GetString;
        JsConfig<byte[]>.DeSerializeFn = Encoders.Hex.GetBytes;

        var fn = JsvReader.GetParseFn(typeof (byte[]));
        var result = fn.Invoke("10ED0F8F7");

It does parse it correctly!

I’ve tracked this deserialization of the DTO to the JsvReader via the StringMapTypeDeserializer, it appears it should ask JsConfig for the DeSerializeFn the same as my example above, but for some reason it returns System.ConvertFromBase64.

Would you mind having a look at this?

Edit: I have tried calling JsvReader.GetParseFn with the serializers correctly set up before any routes come in to stop it caching the wrong method.

Many thanks,
James

1 Like