问题
Background
I want to provide some JsonSchema from my .NET Core 3 application, as well as other objects serialized into JSON. Since Manatee.Json is frequently updated and provides good support for JsonSchema, they are my preferred choice. At the same time, .NET Core 3 provides excellent support for returning objects with magic that transform them into JSON documents.
Example:
public ActionResult<MyFancyClass> MyFancyAction()
{
return new MyFancyClass {
Property1 = "property 1 content",
Property2 = "property 2 content",
};
}
Output:
{
"Property1": "property 1 content",
"Property2": "property 2 content"
}
Problem
.NET Core 3 has internal support for JSON with its System.Text.Json
which is used in the previous example. If I try to serialize Manatee.Json.Schema.JsonSchema
, its internal structure are serialized, not the json schema itself.
Example:
public ActionResult<MyFancyClass2> MyFancyAction2()
{
return new MyFancyClass2 {
Property1 = "property 1 content",
Property1Schema = new JsonSchema()
.Type(JsonSchemaType.String)
};
}
Output:
{
"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"
}
}]
}
I expect this:
{
"Property1": "property 1 content",
"Property1Schema": {
"type": "string",
}
}
Manatee.Json.JsonValue
also have a conflicting inner structure, where System.Text.Json.JsonSerializer
fails to access internal get methods correctly and I get for instance this exception message:
Cannot access value of type Object as type Boolean.
DiscoveryManatee.Json.Schema.JsonSchema
has a .ToJson()
method that can be used to get the correct json schema as a JsonValue
, but then I get the problem I just mentioned with serializing Manatee.Json.JsonValue
.
Question
Does anyone know a way to enable System.Text.Json
to serialize Manatee.Json
structures?
Sidemark
Another way forward is to replace System.Text.Json
altogether (take a look at this question).
回答1:
.NET Core 3 json serialization comes with a lot of configuration options. One of them is to add converters that specify how different types should be serialized. One way forward is to create a JsonConverter
for JsonSchema
and another for JsonValue
.
For JsonSchema
we can implement a JsonSchemaConverter
that when serializing/writing, extracts the json schema as a JsonValue and ask the serializer to serialize that JsonValue instead. Like this:
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();
}
}
}
For JsonValue
we can change it into something System.Text.Json
understands, since it is json after all. One unfortunate approach is to serialize the JsonValue
to a string
, parsing it with for instance JsonDocument.Parse(string)
and serialize its RootElement
property. It feels so unnecessary to go via JsonDocument
, so if anyone finds a better solution that would be great!
A possible implementation can look like this:
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();
}
}
}
They must be registered at Startup.cs
like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new JsonValueConverter());
options.JsonSerializerOptions.Converters.Add(new JsonSchemaConverter());
});
}
来源:https://stackoverflow.com/questions/59855362/serializing-manatee-json-in-net-core-3