Whit Waldo
Whit Waldo

Reputation: 5207

JsonConvert attributes appear to be getting skipped over. Why?

I have a number of properties that I'm trying to use Json.NET to serialize. Complex objects work fine as all I have to put above them are the [JsonProperty("name")] attributes.

Unfortunately, I have been struggling to get this to work with any enums. Ideally, I'd like to be able to define the property by just saying something like:

MyValue = ThisEnum.First;

and have the property within my class automatically serialize the enum to the appropriate changed value name (as this question asked: Control enum value format during (de)serialization via attributes. Unfortunately, I have written a number of custom converters and attempted to apply the suggestions of the people in one of my previous questions (Can't get enum to convert to json properly using Json.NET) and despite the respondents in that thread giving answers that look like they'd work, in my solution, they don't have any effect whatsoever.

For example, given this class of properties:

public class Animal
{
    [JsonProperty("id")]
    public string Id {get;set;}

    [JsonProperty("types")]
    [JsonConverter(typeof(StringEnumConverter))]
    public AnimalType Types {get;set;}

    [JsonProperty("created")]
    [JsonConverter(typeof(MyDateTimeToSecondsSinceEpochConverter))]
    public DateTime Created {get;set;}
}

Within the AnimalType enum:

public enum AnimalType
{
    [EnumMember(Value="black_rhino")]
    BlackRhino,
    [EnumMember(Value="wild_pig")]
    WildPig,
    [EnumMember(Value="chicken")]
    Chicken
}

Within the MyDateTimeToSecondsSinceEpochConverter class:

public class MyDateTimeToSecondsSinceEpochConverter : DateTimeConverterBase
{
    public override WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteRawValue(@"""\/Date(" + ConvertDateTimeToEpoch((DateTime)value).ToString() + @")\/""");
    }

    public override ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null) return null;
        if (reader.TokenType == JsonToken.Integer)
            return ConvertEpochToDateTime((long)reader.Value);
        return DateTime.Parse(reader.Value.ToString());
    }

    public DateTime ConvertEpochToDateTime(long seconds)
    {
        return new DateTime(1970, 1, 1).AddSeconds(seconds);
    }

    public long ConvertDateTimeToEpoch(DateTime dateTime)
    {
        var epochStart = new DateTime(1970,1,1);
        if (dateTime < epochStart) return 0;
        return Convert.ToInt64(dateTime.Subtract(epochStart).TotalSeconds);
    }
}

In my calling method, I can have something like:

var data  = new Animal {
    Id = "123abc",
    Types = AnimalType.BlackRhino,
    Created = new DateTime(2014,3,15)
}

I use a method through which I step through each of the properties of the object with the simple intention of listing each of them and the HTML encoded output of the value and putting them all in a string (that I'll later attach to a URL):

public static string GetString(Animal animal)
{
    var result = "";
    foreach( var property in animal.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
    {
        foreach(var attribute in property.GetCustomAttributes(false))
        {
            var name = (JsonPropertyAttribute)attribute;
            var value = property.GetValue(animal, null);
            if (value != null)
            {
                if (!string.Empty(result))
                    result += "&";
                result += string.Format("{0}={1}", name.PropertyName, HttpUtility.UrlEncode(value.ToString()));
            }
        }
    }
    return result;
}

Unfortunately, when I run this, I see:

'id=abc123&type=BlackRhino&created=3%2f15%2f2014+12%3a00%3a00+AM'

Does anyone have any ideas as to how I can get the two JsonConverters to actually do some converting? The JsonProperty name is obviously changing, but 'BlackRhino' didn't change to 'black_rhino' nor did the DateTime convert to a long.

I'm ideally looking for something in attribute land or using these converters so that I don't have to write a get/set for every single enum I use in the project down the road, but can rather just apply the attribute and be done with it.

Thanks!

Upvotes: 2

Views: 782

Answers (1)

Tim S.
Tim S.

Reputation: 56536

You can have Json.NET turn your object into a string and then into a dictionary, which is a collection of name/value pairs like a query string is.

public static string GetString(Animal animal)
{
    var result = "";
    var serialized = JsonConvert.SerializeObject(animal);
    var dict = JsonConvert.DeserializeObject<IDictionary<string, string>>(serialized);
    foreach (var pair in dict)
    {
        if (!string.IsNullOrEmpty(result))
            result += "&";
        result += string.Format("{0}={1}", pair.Key, HttpUtility.UrlEncode(pair.Value.ToString()));
    }
    return result;
}

With your example, this is:

id=123abc&types=black_rhino&created=%2fDate(1394841600)%2f

Upvotes: 1

Related Questions