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.