JSON.NET изменяет тип системы.Свойство объекта автоматически переходит в System.Строка

#c# #json #json.net #deserialization

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

Вопрос:

У меня есть класс с именем Arg . Свойство ArgValue имеет тип System.Object .

 public class Arg
{
    public string ArgName { get; set; }
    public object ArgValue { get; set; }
}
  

Если я хочу десериализовать JSON-строку следующим образом:

 string json = @"{""ArgName"":""arg"",""ArgValue"":""/something/more/than/a/string/""}";

var jsonSerializerSettings = new JsonSerializerSettings() { };

Arg arg = JsonConvert.DeserializeObject<Arg>(json, jsonSerializerSettings);
  

Я получаю объект Arg с его значениями следующим образом:

  • ArgName имеет тип string -> arg
  • ArgValue имеет тип object {string} -> /something/more/than/a/string/

Я этого не хочу JSON.NET преобразует его в System.String . Если свойство имеет тип System.Object , оно не должно преобразовывать его в строку.

Было бы лучше, если бы он оставил его нетронутым, и значение было бы типа: JToken (или JValue).

Существует ли опция JsonSerializerSettings для настройки такого поведения?

Дополнительная информация:

Строковое значение в свойстве ArgValue «больше, чем строка», и мне нужно преобразовать его позже самостоятельно. JsonConverter здесь не может быть использовано, потому что я не знаю конкретный тип объекта на данный момент. позже я знаю и должен использовать JToken.ToObject<MySpecialType>() . (После чего он будет правильно преобразован a JsonConverter ).

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

1. ну, преобразование между string и object означает отправку распаковки, но в вашем случае json содержит строки, а не объекты, поэтому он уже выполняет то, что вы хотите получить.

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

3. Пожалуйста, укажите, почему вы хотите такого поведения?

4. Строковое значение в свойстве «ArgValue» — «больше, чем строка», и мне нужно преобразовать его позже самостоятельно. JsonConverter здесь не может быть использован, потому что я не знаю конкретный тип объекта на данный момент. позже я знаю, и мне нужно использовать JToken. ToObject<MySpecialType>() . (После чего он будет правильно преобразован с помощью JsonConverter).

Ответ №1:

Как я уже указывал в комментарии, строка json, которую вы хотите десериализовать, будет десериализована в объект класса с двумя свойствами, оба типа string . Это связано с тем, что при указании "ArgName" : "arg" двух кавычек между arg метками указывается значение типа string . Это также случай ArgValue , который сериализуется также как строка.

Выполнив эту строку кода:

 Arg arg = JsonConvert.DeserializeObject<Arg>(json, jsonSerializerSettings);
  

Вы получаете object ( arg ) типа Arg , который точно соответствует классическому объекту типа Arg . Это эквивалентно выполнению:

 Arg arg = new Arg{ ArgName = "arg", ArgValue = "/something/more/than/a/string/" };
  

Строковое присвоение свойства ArgValue , которое ожидает объект, является тем же самым в случае десериализации, это не преобразование (или, лучше сказать, приведение), это только бокс.

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

1. @user437899 извините, как это создается, как с object property ?

2. извините, я нажал «ввод», и комментарий был добавлен до того, как я закончил 🙂

3. На стороне клиента класс создается следующим образом: Arg arg = new Arg{ ArgName = «arg», ArgValue = new QualifiedName(«/something/more/than/a/string/») }; значение имеет тип «namespace.Qualifiedname». На стороне сервера он должен быть того же типа, после всех этапов конвейера. Сначала выполняется синтаксический анализ, а затем он должен использовать реализацию JsonConverter для изменения типа. Но я не знаю тип свойства «ArgValue» во время десериализации / синтаксического анализа.

4. @user437899 Я предлагаю вам сохранить его таким object , каким вы его сделали, потому что, если вы не знаете его тип в тот момент, когда вы десериализуете это свойство, то сохранение в качестве объекта, а затем приведение его после — это единственное, что вы можете сделать. Вы можете попробовать десериализовать как dynamic тип, но это будет менее безопасно.

5. Хорошо, спасибо. единственный способ, который я вижу, — это самостоятельно десериализовать объект на «более низком уровне», а затем я могу решить, как десериализовать свойство. (Оно должно быть типа JToken)

Ответ №2:

Для последующего преобразования вы можете использовать

Общие

 public class Arg<T>
{
    public string ArgName { get; set; }
    public T ArgValue { get; set; }
}
  

затем вы можете передать целевой тип следующим образом:

 JsonConvert.DeserializeObject<Arg<Qualifiedname>>(somestring);
  

или

 public Arg<T> Deserialize<T>(string somestring){
  return JsonConvert.DeserializeObject<Arg<T>>(somestring);
}
  

Оставьте его как JToken

 public class Arg
{
    public string ArgName { get; set; }
    public JToken ArgValue { get; set; }
}
  

Десериализуйте его позже с помощью JsonConvert

 public class Arg
{
    public string ArgName { get; set; }
    public string ArgValue { get; set; }
}
  

или просто передавайте его целиком как строку, пока не узнаете, какой тип будет проанализирован..

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

1. Я не могу сделать свойство JToken, потому что класс «Arg» является классом DTO, который также используется другими форматами передачи.

2. Не могли бы вы использовать Generics или более позднюю десериализацию?