I know from the documentation that I can annotate my POJOs like this:
#ApiModelProperty(value = "pet status in the store", allowableValues = "available,pending,sold")
public String getStatus() {
return status;
}
to produce something like:
"properties": {
...,
"status": {
"type": "string",
"description": "pet status in the store",
"enum": [
"available",
"pending",
"sold"
]
}
}
Image now to implement the method:
#ApiModelProperty(value = "pets in the store")
public Set<String> getPets() {
return pets;
}
which returns a list of pets available in the store. For example, one day it could be ["cats", "dogs", "songbirds"] and then just ["cats", "dogs"] when the songbirds get sold out.
My API would in fact have an endpoint to fetch the list of pets:
http://petShop.foo/pets
Instead of using allowableValues = "cats, dogs, songbirds",
I would like to specify with a Swagger annotation that
that field must contain a value returned by the given endpoint. That is, something like:
#ApiModelProperty(value = "pets in the store", allowableValues = "/pets")
public Set<String> getPets() {...}
This in order to allow my client/front-end to know which values can be use when making a request to,
for example, buying a pet online. Exactly how I could do if I had "enum": ["cats", "dogs", ..]
You may do the following:
Fork Swagger
Extend method processAllowedValues in io.swagger.util.ParameterProcessor class to consume an Enum class in addition to comma separated values. (currently it supports only comma separated values and range)
Use your custom variant of Swagger while building your web application
However, with this method, you'll need to continue maintaining your fork of Swagger.
A Java annotation is a syntactic metadata. It gets processed during compilation and (if it has #Retention(RetentionPolicy.RUNTIME) is specified on it) is available during runtime for reflective access. Hence, there is no direct way of resolving or setting during runtime!
However, there is a way in Java to accomplish what you want - but it's a bit too complex (and uses some undocumented features!). Here is how:
Create a custom annotation ApiModelProperty(one with #Retention(RetentionPolicy.COMPILE)) - this would act as a wrapper for #ApiModelProperty
Write an annotation processor class for above annotation (it must extend from javax.annotation.processing.AbstractProcessor class)
In your annotation processor, inject #ApiModelProperty with values as read from your Enum (this part is fairly complex as you need to traverse through the AST of Enum to get allowed values)
Project Lombok is a good example. It manipulates Java's Abstract Syntax Tree to add new features in Java.
In it's source code, under lombok.javac.handlers, take a look at:
HandleConstructor.addConstructorProperties method to understand how to add annotations in compile time. (using com.sun.tools.javac.tree.JCAnnotation)
HandleVal.visitLocal method to understand how to read literal values.
You can also take a look at this tutorial: Creating Custom Transformations
Related
I am using Feign to hit external APIs, and then to process the externally generated JSON (aka, the response data cannot be modified in any way), and I am trying to bundle these together into an extensible super type. At this point, I am not even sure if what I am trying to do is possible with Jackson / Feign. If it would be much easier to abandon (or heavily restructure) the polymorphism, I think I am also ready to give up on it and just create a bunch of sub classes.
Here are my two main questions, with more context below.
Should I just separate the easily deduced types from the complex types, and have a little more duplicated boiler plate?
How can I create a custom deserializer for the list object I linked? Ideally I would like to have some way to populate the more boiler plate fields less manually -- as an example, it would be great if I could call default deserializers inside it, which would rely more on the standard annotations in other objects.
Ideally, I would like one class, like this:
public final class BillApiResponse {
#Valid
#JsonProperty("response_status")
private boolean responseStatus;
#Valid
#JsonProperty("response_message")
private String responseMessage;
#JsonProperty("response_data")
private BillApiResponseData responseData;
//getters and setters, etc.
}
and then I would to have Jackson automatically map the simpler objects in whatever way is easiest (LoginResponse, LoginError), while I would try to implement a custom handler for the more complex objects (UpdateObject, ListOfObjects).
So, something like this:
#JsonTypeInfo(use = Id.DEDUCTION)
#JsonSubTypes({
#Type(value = BillLoginSuccess.class),
#Type(value = BillErrorResponse.class),
//#Type(value = BillResponseObject[].class) <--- This breaks things when added
})
// #JsonTypeResolver(value = BillResponseTypeResolver.class) <--- Open to using one of
// these if I can figure out how
// #JsonDeserialize(using = BillResponseDeserializer.class) <--- Also open to using a
// custom deserializer, but I would like to keep it only for certain parts
public interface BillApiResponseData {}
Here is a link to the API specification I am trying to hit:
Get a List of Objects
This returns an untyped array of untyped objects. Jackson does not seem to like that the array is untyped, and stops parsing everything there. Once inside, we would have to grab the type from a property.
{
"response_status" : 0,
"response_message" : "Success",
"response_data" : [{
"entity" : "SentPay",
"id" : "stp01AUXGYKCBGFMaqlc"
// More fields
} // More values]
}
Login
This returns a totally new object. Generally not having issues handling this one (until I add support for the above list, and then all of the parsing breaks down as Jackson throws errors).
Update Object
This returns an untyped object. Once again, we would have to go inside and look at the property.
I have tried a number of things, but generally I was not successful (hence I am here!).
These include:
Trying to hook into the lifecycle and take over if I detect an array object. I believe this fails because Jackson throws an error when it sees the array does not have a type associated with it.
SimpleModule customDeserializerModule = new SimpleModule()
.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(
DeserializationConfig config,
BeanDescription beanDesc,
JsonDeserializer<?> defaultDeserializer) {
if (beanDesc.getBeanClass().isArray()) {
return new BillResponseDeserializer(defaultDeserializer);
} else {
return defaultDeserializer;
}
}
});
Custom Deserializers. The issue I have is that it seems to want to route ALL of my deserialization calls into the custom one, and I don't want to have to handle the simpler items, which can be deduced.
TypeIdResolvers / TypeResolvers. Frankly these are confusing me a little bit, and I cannot find a good example online to try out.
I have some JSON schemas which exist in a hierarchy: A extends B extends C. I am generating Java classes from these using jsonschema2pojo and they get generated into a matching class hierarchy.
Because of the way I am generating the classes, I don't have fine-grained control of which annotations can be applied to which fields (i.e. #JsonPropertyOrder)
When I serialize with Jackson, I get something like
{
"propertyOfA": "razz",
"propertyOfA": "jazz",
"propertyOfA": "baz",
"propertyOfB": "bar",
"propertyOfC": "foo"
}
Which is correct since property order has no meaning in JSON. However, my actual messages are very long - thousands of characters - and when browsing the logs it would be much more convenient if the more generic attributes (those from the base schema, schema C), of which there are only a few, came first in the message.
The individual property order within a schema/class doesn't bother me so much, but it would be nice if I could get Jackson to descend the hierarchy first and then backtrack.
{
"propertyOfC": "foo",
"propertyOfB": "bar",
"propertyOfA": "razz",
"propertyOfA": "jazz",
"propertyOfA": "baz"
}
I checked all of the Features and MapperFeatures and the only thing I found to influence the order was SORT_PROPERTIES_ALPHABETICALLY.
Is there anything else I can do at the ObjectMapper-level, or otherwise without changing the class, to influence this order?
You can apply mixin annotations on a class outside of its (generated) source file. E.g.
on a new file, define an interface:
#JsonPropertyOrder({"propertyOfC", "propertyOfB"})
public interface MixinA {
}
and register it with your ObjectMapper:
objectMapper.addMixIn(A.class, MixinA.class);
properties listed in this order annotation go first so you may skip properties of A.
I'm generating Rest endpoints including adding Openapi/Swagger annotations to the generated code.
While it works quite well with basic types, I have some problems with custom classes.
Right now I have a lot of duplicate schema entries for the custom classes (using #Schema(implementation = MyClass.class)) but at least the needed information is there. However I'd like to find a way to remove the duplicate schema entries while retaining the additional information.
On a github-issue discussing the $ref and lack of sibling properties I found an example how you would write it manually in yaml in order to get the result I'm looking for, however I can't figure out how to set the annotations to produce it.
This is how I think the annotation should look like if I follow the example (just to be on the safe side it is added to both the getter and the setter):
import io.swagger.v3.oas.annotations.media.Schema;
...
public class SepaPaymentRequest {
...
#Schema(name = "w307BetrBeg", description = "BETRAG BEGUENSTIGTER ", allOf = { com.diesoftware.services.utils.Betrag.class }, required = true)
public void setW307BetrBeg(final Betrag w307BetrBeg) {
this.w307BetrBeg = w307BetrBeg;
}
...
}
However what I get when I fetch the openapi.yaml (snippet):
w307BetrBeg:
$ref: '#/components/schemas/Betrag'
What I'd like to have:
w307BetrBeg:
title: 'Betrag'
description: 'BETRAG BEGUENSTIGTER'
allOf:
- $ref: '#/components/schemas/Betrag'
Any hints are more than welcome.
I haven't found a way to do it using annotations, i.e. by annotating the class.
I think it's possible to do, by:
Creating a model
Injecting the model using a ModelConverter
When I say "a model" I mean an instance of io.swagger.v3.oas.models.media.Schema.
In particular I think you'd want to create and inject a io.swagger.v3.oas.models.media.ComposedSchema instance, which supports allOf.
Doing this (i.e. creating model instances) isn't very different from hand-writing the YAML.
Another possibility -- which I haven't tried -- might be to write a slightly different ModelConverter, which you install into the chain of converters. Then, intercept calls to resolve which return a SchemaObject whose name is Betrag, and (sometimes?) replace that with a ComposedSchema instance which uses allOf.
My application is receiving JSON messages from a WebSocket connection.
There are different types of answers, which are formatted like that:
{
"type": "snapshot",
"product_id": "BTC-EUR",
"bids": [["1", "2"]],
"asks": [["2", "3"]]
}
or
{
"type": "l2update",
"product_id": "BTC-EUR",
"changes": [
["buy", "1", "3"],
["sell", "3", "1"],
["sell", "2", "2"],
["sell", "4", "0"]
]
}
... for example (see full API here).
Depending on the "type", I would like GSON to map a different class (e.g. Snapshot.class and l2update.class).
I have message handlers that subscribe to the WebSocket connection and I want the message to be processed by the relevant handler. For instance:
ErrorMessageHandler would manage the errors
SnapshotMessageHandler would create the initial order book
L2UpdateMessageHandler would update the order book
and so on
My problem is to dispatch the messages depending on their type.
I was thinking to convert them to the appropriate class and then call the relevant handler using a factory. I'm currently stuck at the first step, converting the JSON in Error.class or Snapshot.class depending on the "type".
How can I do that?
For Gson you could use com.google.gson.typeadapters.RuntimeTypeAdapterFactory.
Assuming you have - for example - following classes:
public class BaseResponse {
private String type, product_id;
// rest of the common fields
}
public class Snapshot extends BaseResponse {
// rest of the fields
}
public class L2Update extends BaseResponse {
// rest of the fields
}
then you would build following RuntimeTypeAdapterFactory:
RuntimeTypeAdapterFactory<BaseResponse> runtimeTypeAdapterFactory =
RuntimeTypeAdapterFactory
.of(BaseResponse.class, "type") // set the field where to look for value
.registerSubtype(L2Update.class, "l2update") // values map to 'type'
.registerSubtype(Snapshot.class, "snapshot");// value in json
Registering this with Gson will then enable automativcal instantiation of each type of responses:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(runtimeTypeAdapterFactory).create();
and provide BaseResponse for fromJson(..) if using it , like:
gson.fromJson( json , BaseResponse.class);
NOTE: that Gson omits de- & serializing the type field. However it needs to be set in Json. Just as it is now in responses you get.
You may want to consider using a library that requires a bit less of a solid object model, at least at first. I use JsonPath for this type of thing. You could use it to at least find out the type you're dealing with:
String type = JsonPath.read(yourIncomingJson, "$.type");
and then, based on the string, do a switch statement as #ShafinMahmud suggests.
However, you could use JsonPath for the whole thing too. You could read all of the values using the path notation and know how to parse based on the type.
Adding another library to read a single value may or may not work for you but if you use it to read other values it might end up being worthwhile.
I have an object. Let's call it `Customer' that is being serialized from a JSON object. Customer has many different fields, but for simplicity let's say that it has twenty (five of which are phone numbers). Is there any sort of convention for validating these fields? I've created one giant method that checks each individual field itself or by calling a method for certain length constraints, email downcasing and validation, phone numbers are stripped of all non-numeric values, length checked, validated, and so on.
All of these methods are held within the Customer class and it's starting to become a little sloppy for my liking. Should I create another class called CustomerValidators? Perhaps several other classes such as EmailValidator, PhoneValidator etc.? Is there any sort of convention here that I'm not aware of?
Try JSR-303 Bean validation. It lets you do things like:
public class Customer {
#Size(min=3, max=5) //standard annotation
private String name;
#PhoneNumber(format="mobile") //Custom validation that you can write
private String mobile;
#PhoneNumber(format="US Landline") //... and reuse, with customisation
private String landline
#Email //Or you can reuse libraries of annotations that others make, like this one from Hibernate Validator
private String emailAddress;
//... ignoring methods
}
The best documentation of this, in my opinion, is for the Hibernate Validator implementation of the spec.
Is your customer object use-case specific? I recommend exposing a use-case specific object for service invocations. The data from this object is then mapped onto your reusable, rich domain objects. (Eg using Dozer).
The reason we have use-case specific objects for service in/out payloads is the "don't spill your guts" principle (aka contract first) - this way you can evolve your application model without effecting service subscribers.
Now the validation:
You can use the annotation-based JSR-303 validation to verify that the input falls within acceptable ranges.
More complex rules are expressed with methods on the rich domain model. Avoid the anaemic domain classes anti-pattern - let them have rich, OO behaviors. To do this they may need to enlist collaborators. Use dependency injection to provide these. DI on non-container managed classes' eg persistent model instances can be achieved using Spring's #Configurable annotation (among other ways).
There is of course the Java EE validation API. How suitable it is for you depends on your environment.
As you already have a JSON structure filled with data that require validation you might have a look into JSON schema. Although it is still in draft it is not that complicated to learn.
A simple JSON schema might look like this:
{
"$schema": "http://json-schema.org/schema#",
"id": "http://your-server/path/to/schema#",
"title": "Name Of The JSON Schema",
"description": "Simple example of a JSON schema",
"definitions": {
"date-time": {
"type": "object",
"description": "Example of a date-time format like '2013-12-30T16:15:00.000'",
"properties": {
"type": "string",
"pattern": "^(2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)[T| ]?([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3}[Z]?)?$"
}
}
},
"type": "object",
"properties": "{
"nameOfField": {
"type": "string"
},
"nameOfSomeIntegerArray": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 0,
"maxItems": 30,
"uniqueItems": true
},
"nameOfADateField": {
"$ref": "#/definitions/date-time"
}
},
"required": [ "nameOfRequiredField1", "nameOfRequiredField2" ],
"additionalProperties": false
}
The definitions part allows the definition of some element you can refer to using "$ref". The URI following "$ref" starts with a # which means it refers to the local schema; http://your-server/path/to/schema so to say. In the sample above it defines a date-time format which can be used to validate JSON fields a reference to the date-time definition has been set for. If the value inside the field does not match the regular expression validation will fail.
In Java a couple of libraries are available. I'm currently using json-schema-validator from Francis Galiegue. Validating a JSON object using this framework is quite simple:
public boolean validateJson(JsonObject json) throws Exception
{
// Convert the JsonObject (or String) to a internal node
final JsonNode instance = JsonLoader.fromString(json.toString());
// Load the JsonSchema
final JsonNode schema = JsonLoader.fromResource("fileNameOfYourJsonSchema");
// Create a validator which uses the latest JSON schema draft
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
final JsonValidator validator = factory.getValidator();
// Validate the JSON object
final ProcessingReport report = validator.validate(schema, instance);
// optional error output
final Iterator<ProcessingMessage> iterator = report.iterator();
while( iterator.hasNext() )
{
final ProcessingMessage message = iterator.next();
System.out.println(message.getMessage());
// more verbose information are available via message.getJson()
}
return report.isSuccess();
}