【发布时间】:2012-02-28 21:54:38
【问题描述】:
在使用 Json.Net 时,我了解如何将 $type 属性放入呈现的 json 中,但是有没有办法更改该字段名称?我需要使用“__type”而不是“$type”。
【问题讨论】:
-
我也需要这个,例如JSON-LD使用
@type
标签: json.net
在使用 Json.Net 时,我了解如何将 $type 属性放入呈现的 json 中,但是有没有办法更改该字段名称?我需要使用“__type”而不是“$type”。
【问题讨论】:
@type
标签: json.net
http://json.codeplex.com/workitem/22429
“我宁愿保持 $type 硬编码和一致。”
与我想知道的一致吗?
http://json.codeplex.com/workitem/21989
我宁愿不 - 我认为这对我来说太具体了,我不 想要过度设置。在某个时候,我可能会 实现这个 - http://json.codeplex.com/workitem/21856 - 允许 人们在 JSON 中读/写自己的元属性,而你 可以使用新的属性名称重新实现类型名称处理。这 其他选择只是为自己修改源代码 该属性名称。
最近,Issue #36: Customizable $type property name feature:
我宁愿不要
这是我的解决方案...
json.Replace("\"$type\": \"", "\"type\": \"");
【讨论】:
看起来这在Newtonsoft.Json.Serialization.JsonTypeReflector 中被硬编码为public const string TypePropertyName = "$type";,不幸的是这是内部静态类。
我自己也需要这个,我唯一能想到的就是拥有 json.net 本身的自定义修改版本。这当然是一个主要的皮塔饼。
【讨论】:
在序列化时,有一个很好的方法来覆盖属性名称:
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\");
【讨论】:
我必须为我的 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());
完成。
【讨论】:
我们有这个需求,所以我创建了一个自定义 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);
}
}
【讨论】:
你也可以这样做:
[JsonConverter(typeof(JsonSubtypes), "ClassName")]
public class Annimal
{
public virtual string ClassName { get; }
public string Color { get; set; }
}
您将需要JsonSubtypes
不属于Newtonsoft.Json 项目的转换器。
【讨论】:
还有另一个选项允许序列化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}
【讨论】:
JsonCreationConverter,例如见stackoverflow.com/questions/8030538
使用自定义转换器应该可以完成工作。
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);
}
}
【讨论】: