JSON.Net - Изменить поле $type на другое имя?
При использовании Json.Net я понимаю, как получить свойство $type в обработанном json, но есть ли способ изменить это имя поля? Мне нужно использовать "__type" вместо "$ type".
Ответы
Ответ 1
Похоже, это жестко закодировано как public const string TypePropertyName = "$type";
в Newtonsoft.Json.Serialization.JsonTypeReflector
, который, к сожалению, является внутренним статическим классом.
Мне это нужно было самому, и единственное, что я могу придумать, - это иметь измененную версию json.net. Это, конечно, крупная лаваш.
Ответ 2
http://json.codeplex.com/workitem/22429
"Я предпочел бы сохранить $type жестко закодированным и последовательным".
В соответствии с тем, что мне интересно?
http://json.codeplex.com/workitem/21989
Я бы предпочел - я думаю, что это слишком специфично для меня, и я не хотите выйти за борт с настройками. В какой-то момент я, вероятно, реализовать это - http://json.codeplex.com/workitem/21856 - разрешить люди читают/пишут собственные мета-свойства в JSON, и вы может переопределить обработку имен типов с новым именем свойства. другой вариант - просто изменить исходный код для себя, чтобы это имя свойства.
Это мое решение...
json.Replace("\"$type\": \"", "\"type\": \"");
Ответ 3
У нас была потребность в этом, поэтому я создал пользовательский JsonReader. Мы используем отдых в наших веб-сервисах MS со сложными моделями данных и должны заменить свойство "__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);
}
}
Ответ 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\");
Ответ 5
Мне пришлось сделать это для моего API интерфейса RI как Angular.js игнорировать имена полей, начинающиеся со знака доллара ($).
Итак, вот решение, которое переименовывает $type
в __type
для всего веб-API и работает как для сериализации, так и для десериализации.
Чтобы иметь возможность использовать пользовательский 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());
Готово.
Ответ 6
Существует еще один параметр, который позволяет сериализовать имя свойства настраиваемого типа в Json.NET
. Идея заключается в том, что мы не указываем свойство default $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
Вы также можете сделать это следующим образом:
[JsonConverter(typeof(JsonSubtypes), "ClassName")]
public class Annimal
{
public virtual string ClassName { get; }
public string Color { get; set; }
}
Вам понадобится JsonSubtypes
который не является частью проекта Newtonsoft.Json
.