Kevin
Kevin

Reputation: 6014

custom serializer for just one property in Json.NET

UPDATE Found the issue -- was inheriting from wrong class, needed to be JsonConverter.

I have a class that has a Location property of type System.Data.Entity.Spatial.DbGeography. The default Json.NET serializer puts out JSON text like this:

  ...
  "PlaceType": 0,
  "Location": {
    "Geography": {
      "CoordinateSystemId": 4326,
      "WellKnownText": "POINT (-88.00000 44.00000)"
    }
  },
  "AddedDT": null,
  ...

I want it to put out text like this:

  ...
  "PlaceType": 0,
  "Location": [-88.00000,44.00000],
  "AddedDT": null,
  ...

...so it seems to me what I should do would be to override whatever converter is currently being used on the DbGeography type.

The examples I've seen so far that use CustomCreationConverters and ContractResolvers seem to address how you'd replace the serializer for the main class being serialized, not for a type that's only a property of that class. The examples that involve annotating the class that's being overridden don't work for me because I don't define DbGeography in my code and it's effectively a sealed class because it has no constructor and can only be instantiated by internal factory methods.

Is there a way to apply a JsonConverter to a type fluently? If so, what would the converter look like? Do I just override the WriteJson() method?

Upvotes: 44

Views: 39109

Answers (3)

bonh
bonh

Reputation: 2956

You can add a custom serializer to a single attribute like this:

public class Comment
{
    public string Author { get; set; }

    [JsonConverter(typeof(NiceDateConverter))]
    public DateTime Date { get; set; }

    public string Text { get; set; }
}

public class NiceDateConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var date = (DateTime) value;
        var niceLookingDate = date.ToString("MMMM dd, yyyy 'at' H:mm tt");
        writer.WriteValue(niceLookingDate);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTime);
    }
}

Then, when you serialize your object with JsonConvert.SerializeObject(), the custom serializer will be used for the Date property.

Upvotes: 60

user2731030
user2731030

Reputation: 101

Use the JsonConverterAttribute on the property and define a custom converter-

for example, we have a property that comes in as a unix value (long int) and we serialize it to a .Net DateTime:

[JsonConverter(typeof(UnixTimeJsonConverter))]
public DateTime Requested { get; set; }

Upvotes: 10

Kevin
Kevin

Reputation: 6014

Turns out I just needed to inherit from JsonConverter instead of CustomCreationConverter, and everything else I was trying to change was OK all along.

I'm still not sure if there's a way to apply the JsonConverter fluently, but there is another way to apply the JsonConverter without referencing Json.NET in your domain/core project or marking up your domain classes with references to a peripheral library:

var jsonSerializer = new JsonSerializer();
jsonSerializer.Converters.Add(new DbGeographyConverter());
jsonSerializer.Serialize(jsonWriter, place);

Upvotes: 10

Related Questions