2

I am deserialize a websocket message in real time. In the message (string of json) I receive there is a unix timestamp (long). As soon as each object is deserialized I need it to call a method ASAP so that I can capture the delay between the time the message was sent and received. With Json.NET that was simple I just added this method to my DataContract class:

[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
    Timestamp = DateTimeOffset.FromUnixTimeMilliseconds(TimestampLong).LocalDateTime;
    Delay = DateTime.Now - Timestamp;
}

I would much prefer to use ServiceStack's deserializer going forward for various reasons but I can't seem to figure out a way to do this. I did find this StackOverflow post from almost 10 years ago but I'm hoping that's changed or that there is a workaround that I can use.

LorneCash
  • 1,446
  • 2
  • 16
  • 30

1 Answers1

1

ServiceStack.Text doesn't support these attributes by default, but you can implement serialization callbacks with Custom Type Configuration, e.g:

JsConfig<MyType>.OnDeserializedFn = o =>
{
    o.OnDeserialized(null);
    return o;
};

The SerializationHookTests.cs shows how you can use the Type Filters to wire up these callbacks for a type using this helper:

static void AddSerializeHooksForType<T>()
{
    Type type = typeof(T);
    System.Reflection.MethodInfo[] typeMethods = type
      .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    var onSerializingMethods = typeMethods.Where(m => 
        m.GetCustomAttributes(typeof(OnSerializingAttribute), true).Length > 0);
    var OnDeserializedMethods = typeMethods.Where(m => 
        m.GetCustomAttributes(typeof(OnDeserializedAttribute), true).Length > 0);
    var OnSerializedMethods = typeMethods.Where(m => 
        m.GetCustomAttributes(typeof(OnSerializedAttribute), true).Length > 0);
    Object[] Parameters = { null };

    if (onSerializingMethods.Any()) {
        ServiceStack.Text.JsConfig<T>.OnSerializingFn = s => {
            foreach (var method in onSerializingMethods)
                method.Invoke(s, Parameters);
            return s;
        };
    }
    if (OnSerializedMethods.Any()) {
        ServiceStack.Text.JsConfig<T>.OnSerializedFn = s => {
            foreach (var method in OnSerializedMethods)
                method.Invoke(s, Parameters);
        };
    }
    if (OnDeserializedMethods.Any()) {
        ServiceStack.Text.JsConfig<T>.OnDeserializedFn = s => {
            foreach (var method in OnDeserializedMethods)
                method.Invoke(s, Parameters);
            return s;
        };
    }
}

Which you can wire up for a type with:

AddSerializeHooksForType<HookTest>();

Where it will call the desired callbacks, e.g:

public class HookTest
{
    /// <summary>
    /// Will be executed when deserializing starts
    /// </summary>
    [OnDeserializing]
    protected void OnDeserializing(StreamingContext ctx) { }

    /// <summary>
    /// Will be executed when deserializing finished
    /// </summary>
    [OnDeserialized]
    protected void OnDeserialized(StreamingContext ctx) { }

    /// <summary>
    /// Will be executed when serializing starts
    /// </summary>
    [OnSerializing]
    protected void OnSerializing(StreamingContext ctx) { }

    /// <summary>
    /// Will be executed when serializing finished
    /// </summary>
    [OnSerialized]
    protected void OnSerialized(StreamingContext ctx) { }
}
mythz
  • 141,670
  • 29
  • 246
  • 390
  • Looks like `OnDeserializingFn` only works when the JSON contains an actual property, see https://dotnetfiddle.net/H4CrWP. So it isn't an equivalent to `[OnDeserializing]`. – dbc Feb 05 '21 at 16:25
  • @dbc I noticed that The `OnDeserializing` components are missing. Can you please post JsConfig.OnDeserializingFn... part I'm having trouble working out what that should look like. – LorneCash Feb 06 '21 at 02:24
  • @LorneCash - looks like `OnDeserializingFn` doesn't work like `[OnDeserializing]`. Rather than being called when an object is being deserialized, it's called for each specific property as that property is being deserialized. The signature seems to be `(object, propertyName, propertyValue)` and it returns a (possibly modified) `propertyValue`. See [here](https://github.com/ServiceStack/ServiceStack.Text/blob/4708869691e85b80e29d8ac0c99eac1345ca3883/src/ServiceStack.Text/Common/DeserializeTypeRefJson.cs#L114). – dbc Feb 06 '21 at 03:53