简体   繁体   中英

How to serialize/deserialize a DateTime stored inside an object field using DataContractJsonSerializer?

I use the following class to exchange JSON data over two ASP.NET services :

[DataContract]
public class Filter
{
   [DataMember]
   public string Name {get; set;}

   [DataMember]
   public FilterOperator Operator {get; set;}

   [DataMember]
   public object Value {get; set;}    
}

Here is the problem : if I set a DateTime inside Value , it will be deserialized as string :

Value = "/Date(1476174483233+0200)/"

This is probably because deserializer has no clue to know what was the type of the value when serialized initially :

JSON = {"Value":"\/Date(1476174483233+0200)\/"}

As explained here , DataContractJsonSerializer supports polymorphism, with the help of the __type property.

I have tried to add [KnownType(typeof(DateTime))] attribute on the top of the class but it does not help.

However if I set a Tuple<DateTime> inside Value property (and the appropriate KnownType attribute on the class), it works (the value it deserialized properly) :

Value = {(10/11/2016 10:49:30 AM)}

Inside JSON, __type is emited

JSON = {
     "Value": {
        "__type" : "TupleOfdateTime:#System",
        "m_Item1" : "\/Date(1476175770028+0200)\/"
     }
}

Is there a way to force DataContractJsonSerializer to emit proper information to serialize/deserialize DateTime properly (which mean I got a DateTime after serialization instead of a string) ?

I have try to set EmitTypeInformation = EmitTypeInformation.Always in DataContractJsonSerializerSettings but it does not help.

The problem is that DataContractJsonSerializer only inserts a polymorphic type hint property "__type" for types that correspond to a JSON object - an unordered set of name/value pairs surrounded by { and } . If the type maps to anything else (ie a JSON array or primitive) then there is no place for a type hint to be inserted. This restriction is documented in Stand-Alone JSON Serialization :

Type Hints Apply Only to Complex Types

There is no way to emit a type hint for non-complex types. For example, if an operation has an Object return type but returns a Circle, the JSON representation can be as shown earlier and the type information is preserved. However, if Uri is returned, the JSON representation is a string and the fact that the string used to represent a Uri is lost. This applies not only to primitive types but also to collections and arrays.

Thus what you will need to do is to modify your Filter class to serialized and deserialized a generic surrogate object for its value that encapsulates the value's type information, along the lines the one in this question for Json.Net :

[DataContract]
public class Filter
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public FilterOperator Operator { get; set; }

    [IgnoreDataMember]
    public object Value { get; set; }

    [DataMember]
    TypedSurrogate TypedValue
    {
        get
        {
            return TypedSurrogate.CreateSurrogate(Value);
        }
        set
        {
            if (value is TypedSurrogate)
                Value = ((TypedSurrogate)value).ObjectValue;
            else
                Value = value;
        }
    }
}

[DataContract]
// Include some well-known primitive types.  Other types can be included at higher levels
[KnownType(typeof(TypedSurrogate<string>))]
[KnownType(typeof(TypedSurrogate<bool>))]
[KnownType(typeof(TypedSurrogate<byte>))]
[KnownType(typeof(TypedSurrogate<sbyte>))]
[KnownType(typeof(TypedSurrogate<char>))]
[KnownType(typeof(TypedSurrogate<short>))]
[KnownType(typeof(TypedSurrogate<ushort>))]
[KnownType(typeof(TypedSurrogate<int>))]
[KnownType(typeof(TypedSurrogate<long>))]
[KnownType(typeof(TypedSurrogate<uint>))]
[KnownType(typeof(TypedSurrogate<ulong>))]
[KnownType(typeof(TypedSurrogate<float>))]
[KnownType(typeof(TypedSurrogate<double>))]
[KnownType(typeof(TypedSurrogate<decimal>))]
[KnownType(typeof(TypedSurrogate<DateTime>))]
[KnownType(typeof(TypedSurrogate<Uri>))]
[KnownType(typeof(TypedSurrogate<Guid>))]
[KnownType(typeof(TypedSurrogate<string[]>))]
public abstract class TypedSurrogate
{
    protected TypedSurrogate() { }

    [IgnoreDataMember]
    public abstract object ObjectValue { get; }

    public static TypedSurrogate CreateSurrogate<T>(T value)
    {
        if (value == null)
            return null;
        var type = value.GetType();
        if (type == typeof(T))
            return new TypedSurrogate<T>(value);
        // Return actual type of subclass
        return (TypedSurrogate)Activator.CreateInstance(typeof(TypedSurrogate<>).MakeGenericType(type), value);
    }
}

[DataContract]
public class TypedSurrogate<T> : TypedSurrogate
{
    public TypedSurrogate() : base() { }

    public TypedSurrogate(T value)
        : base()
    {
        this.Value = value;
    }

    public override object ObjectValue { get { return Value; } }

    [DataMember]
    public T Value { get; set; }
}

Now your JSON will look something like:

{
  "TypedValue": {
    "__type": "TypedSurrogateOfdateTime:#Question39973917",
    "Value": "/Date(1476244800000)/"
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM