Я пытаюсь сериализовать строку JSON, чтобы преобразовать имена свойств в CamelCase, используя библиотеку System.Text.Json. Тем не менее, я все еще получаю результат с помощью PascalCase.
Вот мой код:
var stringJson = "{ \"Key\": \"TextA\" }";
var outcome = SerializeWithCamelCase(stringJson);
private static JsonElement SerializeWithCamelCase(string jsonContent)
{
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true
};
using var document = JsonDocument.Parse(jsonContent);
var element = document.RootElement;
var jsonString = JsonSerializer.Serialize(element, options);
using var camelCaseDocument = JsonDocument.Parse(jsonString);
return camelCaseDocument.RootElement.Clone();
}
Я пробовал много вещей, но с PascalCase все равно получаю результат.
Я ожидаю, что выходные данные будут иметь имена свойств в CamelCase, но я все равно получаю PascalCase.
Ожидаемый результат:
{
"key": "TextA"
}
Фактический результат:
{
"Key": "TextA"
}
Последний тип должен быть JsonElement.
Проверьте дубликат. Невозможно изменить сгенерированный JsonDocument. Если вы десериализовали в правильный класс, политика именования будет работать. Как показывает дубликат, вы можете использовать собственный преобразователь, чтобы принудительно использовать политику. Или вы можете десериализовать в JsonNode вместо JsonDocument и переименовать ключи.
Если вы решите изменить ключи, вы можете использовать JsonNamingPolicy.CamelCase.ConvertName(someString)
нет определенной структуры json. Там может быть что угодно.
В этом случае используйте собственный конвертер в дублирующемся вопросе. Если бы строка JSON была простой, вы могли бы десериализовать ее, например, в JsonObject и начать заменять атрибуты их эквивалентами в верблюжьем регистре. Однако при произвольной структуре вам придется обрабатывать вложенные объекты и массивы объектов на неизвестную глубину. Для этого требуется как минимум рекурсия и гораздо больше кода, чем в пользовательском конвертере.
Кстати, ответ, который вы приняли, - худшая из возможных идей. Нет смысла что-либо клонировать, а тем более дважды анализировать каждый элемент. А многочисленные операции по манипулированию строками приведут к созданию большого количества временных строк. Даже ToCamel
неверно, и JsonNamingPolicy.CamelCase.ConvertName(someString)
уже правильно его реализовал.
Используйте следующий подход:
using System;
using System.Collections.Generic;
using System.Text.Json;
class Program
{
static void Main()
{
var stringJson = "{ \"Key\": \"TextA\" }";
var outcome = SerializeWithCamelCase(stringJson);
Console.WriteLine(outcome);
}
private static string SerializeWithCamelCase(string jsonContent)
{
using var document = JsonDocument.Parse(jsonContent);
var root = document.RootElement;
var modifiedObject = ConvertPropertyNamesToCamelCase(root);
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
return JsonSerializer.Serialize(modifiedObject, options);
}
private static JsonElement ConvertPropertyNamesToCamelCase(JsonElement element)
{
switch (element.ValueKind)
{
case JsonValueKind.Object:
var objBuilder = new Dictionary<string, JsonElement>();
foreach (var property in element.EnumerateObject())
{
var camelCaseName = ToCamelCase(property.Name);
var camelCaseValue = ConvertPropertyNamesToCamelCase(property.Value);
objBuilder[camelCaseName] = camelCaseValue;
}
return JsonDocument.Parse(JsonSerializer.Serialize(objBuilder)).RootElement;
case JsonValueKind.Array:
var arrayBuilder = new List<JsonElement>();
foreach (var item in element.EnumerateArray())
{
var camelCaseItem = ConvertPropertyNamesToCamelCase(item);
arrayBuilder.Add(camelCaseItem);
}
return JsonDocument.Parse(JsonSerializer.Serialize(arrayBuilder)).RootElement;
default:
return element.Clone();
}
}
private static string ToCamelCase(string name)
{
if (string.IsNullOrEmpty(name))
return name;
if (name.Length == 1)
return name.ToLowerInvariant();
return char.ToLowerInvariant(name[0]) + name.Substring(1);
}
}
Приведенный выше подход даст вам результат ниже:
{
"key": "TextA"
}
См. рабочий .netfiddle здесь
На самом деле вопрос не в том, как сериализовать с использованием Camel-Case, а в том, как преобразовать документ с использованием Pascal-Case в Camel-Case.
Проблема вопроса в том, что JsonDocument.Parse
создает JsonDocument, который представляет фактическое содержимое, а не атрибуты, требующие сериализации. Невозможно изменить способ обработки JsonDocument синтаксического анализа или десериализации. JsonDocument доступен только для чтения, поэтому его невозможно изменить.
Для конвертации из Pascal-case в Camel есть два варианта:
Используйте специальный преобразователь типов
Один из вариантов, показанный в этом связанном вопросе, — использовать собственный преобразователь, который фактически использует политику именования. Обратите внимание, что конвертер обрабатывает каждый тип элемента явно:
public class JsonElementConverter : JsonConverter<JsonElement>
{
public override void Write(Utf8JsonWriter writer, JsonElement value, JsonSerializerOptions options)
{
switch (value.ValueKind)
{
case JsonValueKind.Object:
var policy = options.PropertyNamingPolicy;
writer.WriteStartObject();
foreach (var pair in value.EnumerateObject())
{
writer.WritePropertyName(policy?.ConvertName(pair.Name) ?? pair.Name);
Write(writer, pair.Value, options);
}
writer.WriteEndObject();
break;
case JsonValueKind.Array:
writer.WriteStartArray();
foreach (var item in value.EnumerateArray())
Write(writer, item, options);
writer.WriteEndArray();
break;
default:
value.WriteTo(writer);
break;
}
}
public override JsonElement Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using var doc = JsonDocument.ParseValue(ref reader);
return doc.RootElement.Clone();
}
}
При этом политика именования работает:
using var doc = JsonDocument.Parse(jsonString);
JsonNamingPolicy policy = JsonNamingPolicy.CamelCase;
var options = new JsonSerializerOptions
{
Converters = { new JsonElementConverter() },
PropertyNamingPolicy = policy,
WriteIndented = true // Or false, if you prefer
};
var serialized = JsonSerializer.Serialize(doc.RootElement, options);
Использование JsonNode
Другой вариант, упомянутый в соответствующем вопросе, но не реализованный, — это десериализация в JsonNode и изменение ключей. Опять же, каждый тип элемента должен быть обработан. Однако на этот раз рекурсия необходима для обработки всех вложенных элементов.
Несмотря на это, функция Camelize
не такая уж и сложная:
public static JsonNode Camelize(JsonNode node,JsonNamingPolicy policy)
{
if (node is JsonObject obj)
{
var pairs=obj.ToList();
foreach(var pair in pairs)
{
obj.Remove(pair.Key);
obj[policy.ConvertName(pair.Key)]=Camelize(pair.Value,policy);
}
return obj;
}
if (node is JsonArray arr)
{
foreach(var item in arr)
{
Camelize(item,policy);
}
return arr;
}
return node;
}
Это может камелизировать сложный объект:
var json = """
{"Key":"TextA",
"Key2":{"Apples":14},
"Items":[{"Bazinga":"X"},
{"Bananas":{
"Potatoes":3,
"Moo":[1,2,3]}
}
]}
""";
var obj=JsonSerializer.Deserialize<JsonNode>(json);
var x=Camelize(obj,JsonNamingPolicy.CamelCase);
var options=new JsonSerializerOptions{
WriteIndented=true
} ;
Console.WriteLine( JsonSerializer.Serialize(x,options));
Результат
{
"key": "TextA",
"key2": {
"apples": 14
},
"items": [
{
"bazinga": "X"
},
{
"bananas": {
"potatoes": 3,
"moo": [
1,
2,
3
]
}
}
]
}
JsonDocument.Parse
однако не создает объект, а создает документ JSON с уже указанными ключами. При сериализации будет сериализовано содержимое JsonDocument, а не его свойства, поэтому политика именования не применяется.