Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User defined struct and default (de)serialization

I have this simple type defined:

public struct Price : IComparer<Price>, IEquatable<Price> {

    private readonly decimal _value;

    public Price(Price value) {
        _value = value._value;
    }

    public Price(decimal value) {
        _value = value;
    }

    public int Compare(Price x, Price y) {
        return x._value.CompareTo(y._value);
    }

    public int Compare(Price x, decimal y) {
        return x._value.CompareTo(y);
    }

    public int Compare(decimal x, Price y) {
        return x.CompareTo(y._value);
    }

    public bool Equals(Price other) {
        return _value.Equals(other._value);
    }

    public override bool Equals(object obj) {
        if (ReferenceEquals(null, obj))
            return false;
        return obj is Price && Equals((Price)obj);
    }

    public override int GetHashCode() {
        return _value.GetHashCode();
    }

    public static implicit operator decimal(Price p) {
        return p._value;
    }

    public static implicit operator Price(decimal d) {
        return new Price(d);
    }
}

When I'm deserializing a given JSON to Price it works just fine. But when I'm trying to serialize, it returns an empty { }. I mean, assume having this model:

public class Product {
    public string Name { get; set; }
    public Price Price { get; set; }
}

deserializng a JSON like this:

{ "Name": "Some name", "Price": 2.3 }

gives me the correct object. But serializing this sample:

var p = new Product { Name = "Some name", Price = 2.3 }

creates this json:

{ "Name": "Some name", "Price": { } }

So, how and what can I do to tell serializer libraries (e.g. Json.NET and Jil) how to serialize my custom types?

UPDATE:

Sample serializing code, using Json.NET

var s = JsonConvert.SerializeObject(p);

UPDATE2: I dont want to be depend on Json.NET or any other thirdparty libs. So, using JsonConverter in Json.NET is not the answer. Thanks in advance.

like image 586
amiry jd Avatar asked May 08 '17 06:05

amiry jd


2 Answers

Right now the serializer does not know how to serialize your struct and you need to tell it how to do it. This is how to do it for Json.NET

One way is to add JsonPropertyAttribute to _value. That would result in json like this

{"Name":"Some Name","Price":{"_value":2.3}}

Another way would be to create your own JsonConverter that treats your price as a decimal. A simplistic approach could look like this

class PriceConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof( decimal? );
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return serializer.Deserialize( reader ) as Price?;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize( writer, (decimal)((Price)value));
    }
}

And you will need the JsonConverterAttribute on your struct

[JsonConverter(typeof(PriceConverter))]
struct Price
{
    //...
}

This will give you json like this

{"Name":"Some Name","Price":2.3}

Both ways serialize and deserialize fine, but the second one creates nicer to read json.

like image 82
Mats391 Avatar answered Sep 19 '22 16:09

Mats391


You can customize the serialization process by implementing the ISerializable interface on an object. This is particularly useful in cases where the value of a member variable is invalid after deserialization, but you need to provide the variable with a value in order to reconstruct the full state of the object. Implementing ISerializable involves implementing the GetObjectData method and a special constructor that will be used when the object is deserialized.

The sample code below shows how to implement ISerializable.

public struct Price : IComparer<Price>, IEquatable<Price>, ISerializable
{

    private readonly decimal _value;

    public Price(Price value)
    {
        _value = value._value;
    }

    public Price(decimal value)
    {
        _value = value;
    }

    public int Compare(Price x, Price y)
    {
        return x._value.CompareTo(y._value);
    }

    public int Compare(Price x, decimal y)
    {
        return x._value.CompareTo(y);
    }

    public int Compare(decimal x, Price y)
    {
        return x.CompareTo(y._value);
    }

    public bool Equals(Price other)
    {
        return _value.Equals(other._value);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
            return false;
        return obj is Price && Equals((Price)obj);
    }

    public override int GetHashCode()
    {
        return _value.GetHashCode();
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Value", _value);
    }

    public static implicit operator decimal(Price p)
    {
        return p._value;
    }

    public static implicit operator Price(decimal d)
    {
        return new Price(d);
    }
}
like image 22
Ali Jalali Avatar answered Sep 17 '22 16:09

Ali Jalali