Подтвердить что ты не робот

Сериализовать динамическое имя свойства для объекта с помощью JSON.NET

Я использую JSON.NET для сериализации моих объектов для подключения к REST API. Одно из свойств моего объекта, которое должно быть сериализовано для JSON, имеет динамическое имя свойства. Если значение, содержащееся в структуре для этого свойства, является числовым значением, то свойство JSON является "type_id", однако, если это значение является строковым значением, тогда имя свойства JSON является "type_code". Я попытался использовать для этого пользовательский JsonConverter, но я получаю JsonWriterException с этим сообщением при попытке сериализации:

"Token PropertyName в состоянии Свойство приведет к недопустимому объекту JSON. Path ''."

Ниже находится подмножество моего объекта, как показано ниже. Я не указывал имя свойства в моем объекте для этого как такового:

[JsonProperty("title",Required=Required.Always,Order=1)]
public string Title { get; set; }

[JsonProperty("date",Order=3)]
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime Date { get; set; }

[JsonProperty(Order=2)]
[JsonConverter(typeof(TypeIdentifierJsonConverter))]
public TypeIdentifier DocTypeIdentifier { get; set; }

В классе TypeIdentifier у меня есть следующее в моем методе WriteJson():

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    TypeIdentifier docTypeId;
    id= (TypeIdentifier) value;

    writer.WritePropertyName(id.ParameterName);
    writer.WriteValue(id.Value);           
}

Однако я предполагаю, что он по умолчанию не совпадает с именем свойства объекта, а не с моим обычным, что приводит к двум именам свойств для одного значения в строке JSON. Как можно присвоить имя свойства динамически для этого, поскольку тег JsonPropertyAttribute появляется, чтобы вытащить имя свойства объекта, если он явно не указан?

ПРИМЕЧАНИЕ. Этот объект никогда не нужно десериализовать из этого приложения.

EDIT: Этот объект помечен атрибутом [JsonObject(MemberSerialization.OptIn)]

4b9b3361

Ответ 1

A JsonConverter не может установить имя свойства в родительском объекте. Когда вызывается метод конвертера WriteJson, имя свойства уже было записано в JSON; писатель ожидает только значения, которое указывает. Вот почему вы получаете сообщение об ошибке. Чтобы сделать эту работу, пользовательский конвертер должен быть создан для родительского объекта. Тогда этот конвертер будет отвечать за запись имен свойств и значений его дочерних элементов.

Последующий

Можно написать конвертер для родительского объекта, чтобы атрибуты JSON, применяемые к нему, по-прежнему соблюдались, но при этом достигали желаемого результата. Я опишу нижеприведенный подход.

Во-первых, немного настройки. Поскольку вы не сказали, что вызвал ваш класс, я возьму для этого примера, что он называется Document. Нам нужно сделать только одно существенное изменение, и это должно удалить атрибут [JsonConverter] из свойства DocTypeIdentifier. Итак, мы имеем:

[JsonObject(MemberSerialization.OptIn)]
class Document
{
    [JsonProperty("title", Required = Required.Always, Order = 1)]
    public string Title { get; set; }

    [JsonProperty("date", Order = 3)]
    [JsonConverter(typeof(IsoDateTimeConverter))]
    public DateTime Date { get; set; }

    [JsonProperty(Order = 2)]
    public TypeIdentifier DocTypeIdentifier { get; set; }

    public string OtherStuff { get; set; }
}

Вы также не указали код для класса TypeIdentifier, поэтому я просто предполагаю, что он выглядит так: ради примера:

class TypeIdentifier
{
    public string Value { get; set; }
    public string ParameterName { get; set; }
}

С этой точки зрения, мы можем сделать конвертер. Этот подход довольно прост: мы загружаем Document в JObject, пользуясь тем, что он соблюдает применяемые атрибуты, затем возвращаемся и фиксируем сериализацию DocTypeIdentifier, так как для этого требуется специальная обработка. После этого мы выпишем JObject в JsonWriter. Вот код:

class DocumentConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Document));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Document doc = (Document)value;

        // Create a JObject from the document, respecting existing JSON attribs
        JObject jo = JObject.FromObject(value);

        // At this point the DocTypeIdentifier is not serialized correctly.
        // Fix it by replacing the property with the correct name and value.
        JProperty prop = jo.Children<JProperty>()
                           .Where(p => p.Name == "DocTypeIdentifier")
                           .First();

        prop.AddAfterSelf(new JProperty(doc.DocTypeIdentifier.ParameterName, 
                                        doc.DocTypeIdentifier.Value));
        prop.Remove();

        // Write out the JSON
        jo.WriteTo(writer);
    }

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

Теперь у нас есть конвертер, но мы можем не просто украсить класс Document атрибутом [JsonConverter], чтобы его использовать. Если бы мы это сделали, мы закончили бы рекурсивный цикл, когда конвертер попытался использовать себя, когда мы загрузили документ в JObject. Поэтому вместо этого нам нужно создать экземпляр конвертера и передать его в сериализатор через настройки. Метод конвертера CanConvert гарантирует, что он будет использоваться в правильном классе. Метод JObject.FromObject использует другой экземпляр serializer внутри, поэтому он не видит DocumentConverter и, следовательно, не попадает в проблему.

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new DocumentConverter());

string json = JsonConvert.SerializeObject(doc, settings);

Ниже приведена демонстрация конвертера в действии:

class Program
{
    static void Main(string[] args)
    {
        Document doc = new Document
        {
            Title = "How to write a JSON converter",
            Date = DateTime.Today,
            DocTypeIdentifier = new TypeIdentifier
            {
                ParameterName = "type_id",
                Value = "26"
            },
            OtherStuff = "this should not appear in the JSON"
        };

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.Converters.Add(new DocumentConverter());
        settings.Formatting = Formatting.Indented;

        string json = JsonConvert.SerializeObject(doc, settings);
        Console.WriteLine(json);
    }
}

Вот результат выше:

{
  "title": "How to write a JSON converter",
  "type_id": "26",
  "date": "2014-03-28T00:00:00-05:00"
}