Использовать другое свойство из json в пользовательском JsonConverter

#c# #json #json.net #deserialization

#c# #json #json.net #десериализация

Вопрос:

Предположим, что следующий JSON:

 {
  "channel": "751853588527054938"
  "message": "751853745758928908"
}
  

И следующая часть кода:

         [JsonConverter(typeof(TextChannelConverter))]
        [JsonProperty("channel")]
        public ITextChannel Channel;

        [JsonConverter(typeof(UserMessageConverter))]
        [JsonProperty("message")]
        public IUserMessage Message;
  

Что я пытаюсь сделать, так это то, что я хочу иметь пользовательский конвертер для десериализации для обоих этих типов, но проблема в том, что я не могу получить сообщение, если у меня есть только его идентификатор, мне также нужно знать идентификатор канала для этого (точнее, сообщение извлекается с помощью ITextChannel#GetMessageAsync (ulong id)). Есть ли способ получить доступ к идентификатору канала или даже к объекту канала в UserMessageConverter?

Я попытался загрузить весь JSON из программы чтения

     class UserMessageConverter : JsonConverter
    {
        public override bool CanWrite => false;
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();

        public override bool CanConvert(Type objectType) => objectType == typeof(IUserMessage);

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject jobj = JObject.Load(reader);
            return null;
        }

  

Но оно выдает исключение:

 Newtonsoft.Json.JsonReaderException: Error reading JObject from JsonReader. Current JsonReader item is not an object: Integer. Path 'message', line 1, position 75.
  

Для справки, код TextChannelConverter:

         class TextChannelConverter : JsonConverter
        {
        public override bool CanWrite => false;
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();

        public override bool CanConvert(Type objectType) => objectType == typeof(ITextChannel);

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.Value == null)
                return null;

            ulong channelid = ulong.Parse(reader.Value.ToString());

            return Essentials.FireLands.GetChannel(channelid);
        }
  

Комментарии:

1. Это кажется странным способом использования JsonConverter . Почему бы вам просто не десериализовать в строку, что и есть … затем, после десериализации, обработайте данные. Похоже, вы пытаетесь убить 2 зайцев одним выстрелом.. но почему? Скорее всего, вам понадобится выполнять операции ввода-вывода, связанные с ReadJson и ReadJson , не асинхронно, поэтому оно будет заблокировано.

Ответ №1:

Если одно из свойств зависит от другого, то вам нужно будет создать одно JsonConverter , которое обрабатывает родительский объект, а не два отдельных преобразователя для каждого из дочерних свойств.

Вы не сказали, какой класс содержит свойства Channel и Message в вашей модели, поэтому я просто назову его Chat для примера.

Таким образом, у вас было бы:

 [JsonConverter(typeof(ChatConverter))]
class Chat
{    
    public ITextChannel Channel { get; set; }
    public IUserMessage Message { get; set; }
}
  

И тогда ваш конвертер может выглядеть примерно так:

 class ChatConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => objectType == typeof(Chat);
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null)
            return null;

        JObject obj = JObject.Load(reader);
        ulong channelId = (ulong)obj["channel"];
        ulong messageId = (ulong)obj["message"];

        ITextChannel channel = Essentials.FireLands.GetChannel(channelId);
        IUserMessage = channel.GetMessageAsync(messageId).Resu<

        return new Chat { Channel = channel, Message = message };
    }
}