#c# #json.net
Вопрос:
Немного новичок здесь, использующий JSON.Нетто в C# —
string JSONOutput = (JsonConvert.SerializeObject(product));
Когда я сериализую Product
объект в своем решении, я получаю JSON, который выглядит следующим образом :
{
"product":{
"displayStatus":1,
"drugClass":1,
"drugType":1
}
}
Значения displayStatus
, drugClass
и drugType
все из Enum
s, поэтому вывод, который я действительно хочу, это :
{
"product":{
"displayStatus":{
"name":"Visible",
"value":1
},
"drugClass":{
"name":"Generic",
"value":1
},
"drugType":{
"name":"Drug",
"value":1
}
}
}
Кто-нибудь знает, как это сделать? Я смотрел на StringEnumConverter
, но единственное, что я вижу, это то, что он просто возвращает строку Enum
вместо целочисленного значения — в то время как я хотел бы и то, и другое.
Комментарии:
1. Вам нужно написать пользовательский конвертер Json. newtonsoft.com/json/help/html/CustomJsonConverter.htm
2. Зачем тебе и то, и другое? имя и ценность?
Ответ №1:
Обратите внимание, что на самом деле вам не должно понадобиться ничего подобного, есть несколько готовых способов преобразования перечислений. Например StringEnumConverter
. Например
[JsonConverter(typeof(StringEnumConverter))]
Однако из чистого нездорового любопытства и академического интереса
Учитывая преобразователь
public class SomeConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName("name");
writer.WriteValue(value.ToString());
writer.WritePropertyName("value");
writer.WriteValue(Convert.ChangeType(value,typeof(int)));
writer.WriteEndObject();
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
// ill leave this up to your imagination
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Enum);
}
}
Перечисления
public enum Bob
{
one,
two
}
public enum Blerge
{
Asd,
BFG
}
И Класс
public class Product
{
[JsonConverter(typeof(SomeConverter))]
public Bob Bob { get; set; }
[JsonConverter(typeof(SomeConverter))]
public Blerge Blerge { get; set; }
}
Использование
var asd = new Product()
{
Bob = Bob.two,
Blerge = Blerge.Asd
};
var bla = JsonConvert.SerializeObject(asd, Formatting.Indented);
Console.WriteLine(bla);
Выход
{
"Bob": {
"name": "two",
"value": 1
},
"Blerge": {
"name": "Asd",
"value": 0
}
}
Комментарии:
1. Насколько мне известно
StringEnumConverter
, не будет сериализовано как имя, так и значение. Как вы настраиваете это, чтобы излучать и то, и другое?2. @PeterCsala правильно! нет, это не так, но дело в том, что вам все равно никогда не нужно сериализовывать/десериализовывать подобным образом
3. Я понял вашу точку зрения и согласен, но это требование операции.
Ответ №2:
Вы можете воспользоваться преимуществами JsonConverter<T>
, подобными этому:
public class EnumConverter<T> : JsonConverter<T> where T : System.Enum
{
public override T? ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer)
=> throw new NotImplementedException();
public override void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer)
{
var nameAndValue = new { Name = value.ToString("g"), Value = value.ToString("d") };
var semiJson = JObject.FromObject(nameAndValue);
semiJson.WriteTo(writer);
}
}
- Я ограничил
T
то, чтобыSystem.Enum
- В
WriteJson
я создал анонимный тип и использовал строки формата перечисления для получения желаемых значений - Мне пришлось создать a
JObject
из анонимного типа, потомуJsonWriter
что он не поддерживает это напрямую
Затем вы можете украсить свой класс модели следующими атрибутами:
public class Product
{
[JsonConverter(typeof(EnumConverter<DisplayStatus>))]
public DisplayStatus DisplayStatus { get; set; }
[JsonConverter(typeof(EnumConverter<DrugClass>))]
public DrugClass DrugClass { get; set; }
[JsonConverter(typeof(EnumConverter<DrugType>))]
public DrugType DrugType { get; set; }
}
И, наконец, логика сериализации выглядит следующим образом:
var product = new Product()
{
DisplayStatus = DisplayStatus.Visible,
DrugClass = DrugClass.Generic,
DrugType = DrugType.Drug,
};
var result = JsonConvert.SerializeObject(
new { Product = product },
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
result
Будет выглядеть так:
{
"product":{
"displayStatus":{
"Name":"Visible",
"Value":"1"
},
"drugClass":{
"Name":"Generic",
"Value":"1"
},
"drugType":{
"Name":"Drug",
"Value":"1"
}
}
}
Для того, чтобы закрепить корпус Name
, и Value
все, что вам нужно сделать serializer
, это передать FromObject
:
var details = JObject.FromObject(nameAndValue, serializer);
После этого конечный результат будет выглядеть так, как и ожидалось:
{
"product":{
"displayStatus":{
"name":"Visible",
"value":"1"
},
"drugClass":{
"name":"Generic",
"value":"1"
},
"drugType":{
"name":"Drug",
"value":"1"
}
}
}
UDPATE #1: Добавьте конвертеры в настройки
Как было указано в комментарии Product
, класс не может быть изменен на JsonConverterAttribute
s. Итак, если у вас есть это ограничение, вы можете преодолеть его, передав преобразователи через JsonSettings
объект следующим образом:
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new JsonConverter[] {
new EnumConverter<DisplayStatus>(),
new EnumConverter<DrugClass>(),
new EnumConverter<DrugType>()
}
};
var result = JsonConvert.SerializeObject(new { Product = product }, settings);
При таком подходе ваша модель будет свободна от атрибутов сериализации 🙂
Комментарии:
1. Большое спасибо за ваше объяснение выше. Однако я не могу украсить свойства
[JsonConverter(typeof(EnumConverter<DrugType>))]
атрибутами, так как они являются частью другой сборки. Есть ли способ обойти это без использования атрибутов?2. @DIamondGeezer Мы можем довольно легко преодолеть это ограничение.
JsonSerializerSettings
У него естьConverters
свойство, в котором мы можем определить пользовательские преобразователи. Дайте мне минуту, и я обновлю сообщение.3. @DIamondGeezer Я только что продлил его, пожалуйста, проверьте это.