JSON.Net - 将$ type字段更改为其他名称?

时间:2012-02-28 21:54:38

标签: json.net

当使用Json.Net时,我理解如何将$ type属性放入渲染的json中,但有没有办法更改该字段名称?我需要使用“__type”而不是“$ type”。

8 个答案:

答案 0 :(得分:17)

看起来这是public const string TypePropertyName = "$type";中的Newtonsoft.Json.Serialization.JsonTypeReflector硬编码,遗憾的是内部静态类。

我自己需要这个,我唯一能想到的就是拥有json.net本身的自定义修改版本。这当然是一个主要的皮塔饼。

答案 1 :(得分:16)

http://json.codeplex.com/workitem/22429

  

“我宁愿保持$ type硬编码和一致。”

与我的想法一致吗?

http://json.codeplex.com/workitem/21989

  

我宁愿不 - 我觉得这对我来说太具体了,我没有   想要超越设置。在某些时候我可能会   实现这一点 - http://json.codeplex.com/workitem/21856 - 允许   人们在JSON中读取/写入自己的元属性和你   可以使用新的属性名称重新实现类型名称处理。该   其他选项只是修改自己拥有的源代码   属性名称。

这是我的解决方案......

json.Replace("\"$type\": \"", "\"type\": \"");

答案 2 :(得分:4)

我们需要这个,所以我创建了一个自定义的JsonReader。我们在具有复杂数据模型的MS Web服务中使用rest,并且需要将“__type”属性替换为“$ type”。

class MSJsonReader : JsonTextReader
{
    public MSJsonTextReader(TextReader reader) : base(reader) { }

    public override bool Read()
    {
        var hasToken = base.Read();

        if (hasToken && base.TokenType == JsonToken.PropertyName && base.Value != null && base.Value.Equals("__type"))
            base.SetToken(JsonToken.PropertyName, "$type");

        return hasToken;
    }
}

以下是我们如何使用它。

using(JsonReader jr = new MSJsonTextReader(sr))
{
    JsonSerializer s = new JsonSerializer();
    s.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
    s.NullValueHandling = NullValueHandling.Ignore;
    s.TypeNameHandling = TypeNameHandling.Auto; // Important!
    s.Binder = new MSRestToJsonDotNetSerializationBinder("Server.DataModelsNamespace", "Client.GeneratedModelsNamespace");

    T deserialized = s.Deserialize<T>(jr);

    return deserialized;
}

这是我们的MSRestToJsonDotNetSerializationBinder,它完成了MS rest和Json.Net之间的兼容性。

class MSRestToJsonDotNetSerializationBinder : System.Runtime.Serialization.SerializationBinder
{
    public string ServiceNamespace { get; set; }
    public string LocalNamespace { get; set; }

    public MSRestToJsonDotNetSerializationBinder(string serviceNamespace, string localNamespace)
    {
        if (serviceNamespace.EndsWith("."))
            serviceNamespace = serviceNamespace.Substring(0, -1);

        if(localNamespace.EndsWith("."))
            localNamespace = localNamespace.Substring(0, -1);

        ServiceNamespace = serviceNamespace;
        LocalNamespace = localNamespace;
    }

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        assemblyName = null;
        typeName = string.Format("{0}:#{1}", serializedType.Name, ServiceNamespace); // MS format
    }

    public override Type BindToType(string assemblyName, string typeName)
    {
        string jsonDotNetType = string.Format("{0}.{1}", LocalNamespace, typeName.Substring(0, typeName.IndexOf(":#")));
        return Type.GetType(jsonDotNetType);
    }
}

答案 3 :(得分:4)

序列化时,有一种很好的方法来覆盖属性名称:

public class CustomJsonWriter : JsonTextWriter
{
    public CustomJsonWriter(TextWriter writer) : base(writer)
    {
    }

    public override void WritePropertyName(string name, bool escape)
    {
        if (name == "$type") name = "__type";
        base.WritePropertyName(name, escape);
    }
}

var serializer = new JsonSerializer();
var writer = new StreamWriter(stream) { AutoFlush = true };
serializer.Serialize(new CustomJsonWriter(writer), objectToSerialize);

我还没有尝试反序列化,但在最坏的情况下我可以使用:

json.Replace("\"__type": \"", "\"type\": \"$type\");

答案 4 :(得分:4)

我必须为我的UI REST API执行此操作,因为Angular.js disregards字段名称以美元符号($)开头。

因此,这是一个解决方案,可以为整个Web API重命名$type__type,同时适用于序列化反序列化。

为了能够使用自定义JsonWriter和自定义JsonReader(正如此问题的其他答案中所提议的那样),我们必须继承JsonMediaTypeFormatter并覆盖相应的方法:

internal class CustomJsonNetFormatter : JsonMediaTypeFormatter
{
    public override JsonReader CreateJsonReader(Type type, Stream readStream, Encoding effectiveEncoding)
    {
        return new CustomJsonReader(readStream, effectiveEncoding);
    }

    public override JsonWriter CreateJsonWriter(Type type, Stream writeStream, Encoding effectiveEncoding)
    {
        return new CustomJsonWriter(writeStream, effectiveEncoding);
    }

    private class CustomJsonWriter : JsonTextWriter
    {
        public CustomJsonWriter(Stream writeStream, Encoding effectiveEncoding)
            : base(new StreamWriter(writeStream, effectiveEncoding))
        {
        }

        public override void WritePropertyName(string name, bool escape)
        {
            if (name == "$type") name = "__type";
            base.WritePropertyName(name, escape);
        }
    }

    private class CustomJsonReader : JsonTextReader
    {
        public CustomJsonReader(Stream readStream, Encoding effectiveEncoding)
            : base(new StreamReader(readStream, effectiveEncoding))
        {
        }

        public override bool Read()
        {
            var hasToken = base.Read();
            if (hasToken && TokenType == JsonToken.PropertyName && Value != null && Value.Equals("__type"))
            {
                SetToken(JsonToken.PropertyName, "$type");
            }
            return hasToken;
        }
    }
}

当然,您需要在WebApiConfig中注册自定义格式化程序。所以我们用我们的自定义格式化代替默认的Json.NET格式化程序:

config.Formatters.Remove(config.Formatters.JsonFormatter);
config.Formatters.Add(new CustomJsonNetFormatter());

完成。

答案 5 :(得分:2)

你也可以这样做:

[JsonConverter(typeof(JsonSubtypes), "ClassName")]
public class Annimal
{
    public virtual string ClassName { get; }
    public string Color { get; set; }
}

您需要JsonSubtypes  转换器不属于Newtonsoft.Json项目。

答案 6 :(得分:1)

还有另一个选项允许在Json.NET中序列化自定义类型属性名称。这个想法是不写默认的$type属性,而是将类型名称作为类本身的属性引入。

假设我们有一个Location类:

public class Location
{
    public double Latitude { get; set; }

    public double Longitude { get; set; }
}

首先,我们需要引入类型属性名称并修改类,如下所示:

public class Location
{
    [JsonProperty("__type")]
    public string EntityTypeName
    {

        get
        {
            var typeName = string.Format("{0}, {1}", GetType().FullName, GetType().Namespace);
            return typeName;
        }
    }

    public double Latitude { get; set; }

    public double Longitude { get; set; }
}

然后,将JsonSerializerSettings.TypeNameHandling设置为TypeNameHandling.None,以便反序列化器跳过默认$type属性的呈现。

就是这样。

示例

var point = new Location() { Latitude = 51.5033630, Longitude = -0.1276250 };

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

<强>结果

{"__type":"Namespace.Location, Namespace","Latitude":51.503363,"Longitude":-0.127625}

答案 7 :(得分:0)

使用自定义转换器可以完成工作。

public CustomConverter : JsonConverter
{
    public override bool CanWrite => true;
    public override bool CanRead => true;

    public override object ReadJson(JsonReader reader,
        Type objectType,
        object existingValue,
        JsonSerializer serializer)
        => throw new NotImplementedException();

    public override void WriteJson(JsonWriter writer,
        object value,
        JsonSerializer serializer)
    {
        var jOjbect = (JObject)JToken.FromObject(value);
        jOjbect.Add(new JProperty("type", value.GetType().Name));
        jOjbect.WriteTo(writer);
    }
}