简体   繁体   中英

(De-)Serialize Known Types similar to Microsoft

Up to now I've used Microsoft's DataContractJsonSerializer to serialize and deserialize my business objects into data transfer objects (DTO) formatted as JSON. The DTOs are marked with the DataContract attribute. A small example:

[DataContract(Name = "Geometry", Namespace = "myContract.com/dto")]
[KnownType(typeof(Point))]
[KnownType(typeof(Line))]
public class Geometry
{
}

[DataContract(Name = "Point", Namespace = "myContract.com/dto")]
public class Point : Geometry
{
    [DataMember(Name = "x")]
    public double X { get; set; }

    [DataMember(Name = "y")]
    public double Y { get; set; }
}

[DataContract(Name = "Line", Namespace = "myContract.com/dto")]
public class Line: Geometry
{
    [DataMember(Name = "start")]
    public Point Start { get; set; }

    [DataMember(Name = "end")]
    public Point End { get; set; }
}

This gets serialized as:

"geometry":{"__type":"Point:myContract.com/dto","x":23133.75569999963,"y":21582.385849999264}

Because of performance issues I switched to Newtonsoft Json.NET. When using this, the JSON strings looks like this:

"geometry":{"$type":"A.B.C.Point, A.B.C","x":23133.75569999963,"y":21582.385849999264}

Is there are possibility to serialize the object with Json.NET into a Microsoft-conform JSON string using "__type" and the contract namespace instead of "$type" and the class-assembly-combination? I'm using .NET 3.5.

Thanks in advance!

Unfortunately it does not seem possible to adjust property name since it is declared in internal JsonTypeReflector.cs class as:

public const string TypePropertyName = "$type";

Note: from another hand, type property value could be customized, SerializationBinder Class is intended for that purpose, this example demonstrates how to specify custom type property value.


I would propose the following solution that allows to write custom type property . First, we need to introduce base entity class (or modify Geometry class) with type property as demonstrated below

[DataContract(Name = "Entity", Namespace = "myContract.com/dto")]
public abstract class Entity
{
    [DataMember(Name = "__type", Order = 0)]
    public string EntityTypeName
    {

        get
        {
            var typeName = GetType().Name;
            if (Attribute.IsDefined(GetType(), typeof(DataContractAttribute)))
            {
                var attribute = GetType().GetCustomAttributes(typeof(DataContractAttribute), true).FirstOrDefault() as DataContractAttribute;
                if (attribute != null) typeName = typeName + ":" + attribute.Namespace;
            }
            return typeName;
        }
    }
}

Then modify Geometry class:

[DataContract(Name = "Geometry", Namespace = "myContract.com/dto")]
[KnownType(typeof(Point))]
[KnownType(typeof(Line))]
public class Geometry : Entity
{
}

And the last step, set JsonSerializerSettings.TypeNameHandling to TypeNameHandling.None in order the deserializer to skip the rendering of $type attribute.

Example

  var point = new Point { X = 23133.75569999963, Y = 21582.385849999264 };

  var jsonPoint = JsonConvert.SerializeObject(point, new JsonSerializerSettings
  {
       TypeNameHandling = TypeNameHandling.None,   //do not write type property(!)
  });
  Console.WriteLine(jsonPoint);

Result

{\"__type\":\"Point:myContract.com/dto\",\"x\":23133.75569999963,\"y\":21582.385849999264}

PS

You could also specify the order using DataMember.Order as shown below:

[DataContract(Name = "Point", Namespace = "myContract.com/dto")]
public class Point : Geometry
{
    [DataMember(Name = "x",Order = 1)]
    public double X { get; set; }

    [DataMember(Name = "y", Order = 2)]
    public double Y { get; set; }
}

[DataContract(Name = "Line", Namespace = "myContract.com/dto")]
public class Line : Geometry
{
    [DataMember(Name = "start", Order = 1)]
    public Point Start { get; set; }

    [DataMember(Name = "end", Order = 2)]
    public Point End { get; set; }
}

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