Сериализация ламантина. Json дюйм. NET Core 3 - PullRequest
0 голосов
/ 22 января 2020

Фон
Я хочу предоставить JsonSchema из моего приложения. NET Core 3, а также другие объекты, сериализованные в JSON. Поскольку Manatee. Json часто обновляется и обеспечивает хорошую поддержку JsonSchema, они - мой предпочтительный выбор. В то же время. NET Core 3 обеспечивает отличную поддержку для возврата объектов с магией c, которые преобразуют их в JSON документы.

Пример:

public ActionResult<MyFancyClass> MyFancyAction()
{
    return new MyFancyClass {
        Property1 = "property 1 content",
        Property2 = "property 2 content",
    };
}

Вывод:

{
    "Property1": "property 1 content",
    "Property2": "property 2 content"
}

Проблема
. NET Core 3 имеет внутреннюю поддержку JSON с его System.Text.Json, который используется в предыдущем примере. Если я попытаюсь сериализовать Manatee.Json.Schema.JsonSchema, то его внутренняя структура сериализуется, а не сама схема json.

Пример:

public ActionResult<MyFancyClass2> MyFancyAction2()
{
    return new MyFancyClass2 {
        Property1 = "property 1 content",
        Property1Schema = new JsonSchema()
            .Type(JsonSchemaType.String)
    };
}

Вывод:

{
  "Property1": "property 1 content",
  "Property1Schema": [{
    "name":"type",
    "supportedVersions":15,
    "validationSequence":1,
    "vocabulary": {
      "id":"https://json-schema.org/draft/2019-09/vocab/validation",
      "metaSchemaId":"https://json-schema.org/draft/2019-09/meta/validation"
    }
  }]
}

Я ожидаю, что:

{
  "Property1": "property 1 content",
  "Property1Schema": {
    "type": "string",
  }
}

Manatee.Json.JsonValue также имеют конфликтующую внутреннюю структуру, где System.Text.Json.JsonSerializer не может правильно получить доступ к внутренним методам get, и я получаю, например, это сообщение об исключении:

Cannot access value of type Object as type Boolean.

Discovery
Manatee.Json.Schema.JsonSchema имеет метод .ToJson(), который можно использовать для получения правильной схемы json в виде JsonValue, но затем я получаю проблему, с которой я только что упомянул сериализация Manatee.Json.JsonValue.

Вопрос
Кто-нибудь знает способ включения System.Text.Json для сериализации Manatee.Json структур?

Sidemark
Другой путь вперед - заменить System.Text.Json в целом (взгляните на этот вопрос ).

1 Ответ

0 голосов
/ 22 января 2020

. NET Сериализация Core 3 json поставляется с большим количеством опций конфигурации. Один из них - добавить конвертеры, которые определяют, как разные типы должны быть сериализованы. Один путь вперед - создать JsonConverter для JsonSchema, а другой для JsonValue.

Для JsonSchema мы можем реализовать JsonSchemaConverter, который при сериализации / записи извлекает схему json как JsonValue и попросить сериализатор вместо этого сериализовать JsonValue. Например:

public class JsonSchemaConverter : JsonConverter<JsonSchema>
{
    public JsonSchemaConverter()
    {
        _manateeSerializer = new ManateeSerializer();
    }

    private ManateeSerializer _manateeSerializer { get; set; }

    public override JsonSchema Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var jsonText = reader.GetString();
        var jsonValue = JsonValue.Parse(jsonText);
        return _manateeSerializer.Deserialize<JsonSchema>(jsonValue);
    }

    public override void Write(Utf8JsonWriter writer, JsonSchema value, JsonSerializerOptions options)
    {
        var schemaAsJson = value.ToJson(_manateeSerializer);
        try
        {
            System.Text.Json.JsonSerializer.Serialize<JsonValue>(writer, schemaAsJson, options);
        }
        catch (Exception e)
        {
            Log.Information($"Failed to serialize JsonSchema ({e.Message});");
            writer.WriteNullValue();
        }
    }
}

Для JsonValue мы можем изменить это на что-то, что System.Text.Json понимает, поскольку в конце концов это json. Один неудачный подход состоит в том, чтобы сериализовать JsonValue в string, анализируя его, например, JsonDocument.Parse(string) и сериализовать его свойство RootElement. go через JsonDocument кажется таким ненужным, поэтому, если кто-то найдет лучшее решение, это было бы здорово! Возможная реализация может выглядеть так:

public class JsonValueConverter : JsonConverter<JsonValue>
{
    public override JsonValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var json = reader.GetString();
        return JsonValue.Parse(json);
    }

    public override void Write(Utf8JsonWriter writer, JsonValue value, JsonSerializerOptions options)
    {
        string content = value.ToString();
        try
        {
            var jsonDocument = JsonDocument.Parse(content);
            JsonSerializer.Serialize<JsonElement>(writer, jsonDocument.RootElement, options);
        }
        catch (Exception e)
        {
            Log.Warning($"JsonDocument.Parse(JsonValue) failed in JsonValueConverter.Write(,,).\n{e.Message}");
            writer.WriteNullValue();
        }
    }
}

Они должны быть зарегистрированы на Startup.cs следующим образом:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddJsonOptions(options =>
        {
            options.JsonSerializerOptions.Converters.Add(new JsonValueConverter());
            options.JsonSerializerOptions.Converters.Add(new JsonSchemaConverter());
        });
}
...