Matías Fidemraizer
Matías Fidemraizer

Reputation: 64933

Declared properties won't be serialized if its declaring type is a dynamic object

Let's say I've the following dynamic object:

public class SomeDynamicObject : DynamicObject
{
     public string Text { get; set; }
}

If I serialize it using JsonConvert.SerializeObject(new SomeDynamicObject { Text = "hello world" }) it'll return {} instead of { "Text": "hello world" }.

I suspect the issue is that JSON.NET thinks it's a full dynamic object while my case is a dynamic object with declared members.

Is there any serialization settings or built-in converter that could be configured so JSON.NET can serialize both kinds of members?

To avoid confusion

Actual use case: I don't know which will be the types being serialized but I need to cover the whole use case of serializing declared properties of a dynamic object.

That is, I can't use attributes. That's why I'm asking if there's some converter or a serialization setting that can generalize this use case.

Upvotes: 2

Views: 136

Answers (1)

David L
David L

Reputation: 33815

Update for non-attribute converter

Since you can't decorate, you lose a lot of power. Once the JsonWriter has converted to a JObject, the dynamic properties appear to be lost.

However, you can always use a little reflection in a custom converter's WriteJson method to serialize non-dynamic types.

public class SomeDynamicObject : DynamicObject
{
    public string Text { get; set; }
    public DynamicObject DynamicProperty { get; set; }
}

public class CustomDynamicConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        JObject jObject = JObject.Load(reader);

        var target = Activator.CreateInstance(objectType);

        //Create a new reader for this jObject, and set all properties to match the original reader.
        JsonReader jObjectReader = jObject.CreateReader();
        jObjectReader.Culture = reader.Culture;
        jObjectReader.DateParseHandling = reader.DateParseHandling;
        jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
        jObjectReader.FloatParseHandling = reader.FloatParseHandling;

        // Populate the object properties
        serializer.Populate(jObjectReader, target);

        return target;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var properties = value.GetType().GetProperties().Where(x => x.PropertyType != typeof(DynamicObject)).ToList();
        JObject o = (JObject)JToken.FromObject(value);

        properties.ForEach(x =>
        {
            o.AddFirst(new JProperty(x.Name, x.GetValue(value)));
        });

        o.WriteTo(writer);
    }
}

If you explicitly decorate your properties with [JsonProperty], the serializer will pick them up, even if the containing type is dynamic.

public class SomeDynamicObject : DynamicObject
{
    [JsonProperty]
    public string Text { get; set; }
}

when serialized correctly outputs:

{"Text":"hello world"}

Upvotes: 4

Related Questions