Я хотел бы создать пользовательские ключевые слова свойств, которые будут предварительно заданы в схеме JSON, сгенерированной с помощью Джексона. Это будет похоже на то, что делает аннотация JsonPropertyDescription, но для пользовательских аннотаций и ключевых слов. Например, я хотел бы иметь возможность аннотировать свойство с помощью пользовательской аннотации JsonPropertyLabel, которая добавит ключевое слово «label» в описание свойства.
Мне удалось сделать аналогичную вещь с помощью пользовательской JsonSchemaи JsonSchemaFactory, как это:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
import com.fasterxml.jackson.module.jsonSchema.customProperties.ValidationSchemaFactoryWrapper;
import com.fasterxml.jackson.module.jsonSchema.factories.FormatVisitorFactory;
import com.fasterxml.jackson.module.jsonSchema.factories.JsonSchemaFactory;
import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
import com.fasterxml.jackson.module.jsonSchema.factories.VisitorContext;
import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory;
import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;
import com.fasterxml.jackson.annotation.JacksonAnnotation;
import com.fasterxml.jackson.module.jsonSchema.validation.AnnotationConstraintResolver;
public class SchemaUtils {
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotation
public @interface JsonPropertyLabel {
String value();
}
public static String getPrettyClassSchema(Class type) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(ConfigurationUtils.getJsonSchema(type, objectMapper));
}
static JsonSchema getJsonSchema(Class type, ObjectMapper objectMapper) throws JsonMappingException {
SchemaFactoryWrapper schemaFactoryWrapper = new CustomJsonSchemaFactoryWrapper();
objectMapper.acceptJsonFormatVisitor(objectMapper.constructType(type), schemaFactoryWrapper);
return schemaFactoryWrapper.finalSchema();
}
static class CustomJsonSchemaFactoryWrapper extends ValidationSchemaFactoryWrapper {
private static class SchemaFactoryWrapperFactory extends WrapperFactory {
private SchemaFactoryWrapperFactory() {
}
public SchemaFactoryWrapper getWrapper(SerializerProvider p) {
return this.getWrapper(p, null);
}
public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) {
SchemaFactoryWrapper wrapper = new CustomJsonSchemaFactoryWrapper();
wrapper.setProvider(p);
if(rvc != null) {
wrapper.setVisitorContext(rvc);
}
return wrapper;
}
}
public CustomJsonSchemaFactoryWrapper() {
super(new AnnotationConstraintResolver());
schemaProvider = new CustomJsonSchemaFactory();
visitorFactory = new FormatVisitorFactory(new SchemaFactoryWrapperFactory());
}
static class CustomJsonSchemaFactory extends JsonSchemaFactory {
@Override
public ObjectSchema objectSchema() {
return new CustomJsonSchema();
}
static class CustomJsonSchema extends ObjectSchema {
@JsonProperty private String label;
@Override
public void enrichWithBeanProperty(BeanProperty beanProperty) {
super.enrichWithBeanProperty(beanProperty);
JsonPropertyLabel labelAnnotation = beanProperty.getAnnotation(JsonPropertyLabel.class);
if (labelAnnotation != null) {
this.label = labelAnnotation.value();
}
}
}
}
}
}
Однако при попытке этого кода в следующем классе:
public class POJOExample {
@JsonPropertyDescription("first property description")
@JsonPropertyLabel("first property label")
public PropertyExample first;
@JsonPropertyDescription("second property description")
@JsonPropertyLabel("second property label")
public PropertyExample second;
public static class PropertyExample {
public String value;
}
}
Схема JSON для этого выглядит так:
{
"type" : "object",
"id" : "urn:jsonschema:test:POJOExample",
"properties" : {
"first" : {
"type" : "object",
"id" : "urn:jsonschema:test:POJOExample:PropertyExample",
"description" : "first property description",
"properties" : {
"value" : {
"type" : "string"
}
},
"label" : "first property label"
},
"second" : {
"type" : "object",
"$ref" : "urn:jsonschema:test:POJOExample:PropertyExample",
"description" : "second property description"
}
}
}
Как видите, у «first» есть как «description», так и «label», а у «second» только «description». Я предполагаю, что эта реализация связывает «метку» как функцию типа, доступную через ссылку. Как бы я сделал так, чтобы «метка» велась как «описание» и содержала бы его и во «втором» свойстве?
Кроме того, было бы оптимальным, если бы я мог сделать это динамически для случаев использования, когдаЯ не знаю значения строки ключевого слова заранее, но во время выполнения. Например, имея аннотацию JsonPropertyKeyword примерно так:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotation
public @interface JsonPropertyKeyword {
String keyword();
String value();
}
Таким образом, я мог бы аннотировать свойства с помощью @JsonPropertyKeyword(keyword="description", value="some description")
и достичь того же эффекта, что и с @JsonPropertyDescription("some description")
. Это возможно?