I am writing a Gradle project with Springfox-swagger-ui 3.0.0 to present nice swagger UI with Api documentation.
compileOnly "io.springfox:springfox-swagger-ui:3.0.0"
One of api endpoints returns a POJO class, that is an auto-generated class from .xsd schema using XJC task.
What I'd like to do is to prevent Swagger from including auto-generated Xml-annotations
Example POJO class (generated from xsd schema):
#Getter
public class TestPojo {
#XmlElement(required = true)
protected String machineSerialNumber;
}
Generated api-docs:
"TestPojo": {
"type": "object",
"title": "TestPojo",
"properties": {
"machineSerialNumber": {
"type": "string",
"xml": {
"name": "machineSerialNumber",
"attribute": false,
"wrapped": false
}
}
}
}
What I'd like to achieve, is to not present these xml-annotations, so that api-docs content will look like this:
"TestPojo": {
"type": "object",
"properties": {
"machineSerialNumber": {
"type": "string"
}
},
"title": "TestPojo"
}
And swagger-ui model:
The problem here is that I cannot change the content of auto-generated classes to remove these annotations.
Is there a way to configure Swagger in a way so that it skips these annotations?
Related
I'm stuck trying to understand how jackson-dataformat-avro resolves type IDs. I have serialized a class successfully and now try to deserialize it again, with the same schema. It has a private field "name" of type LocalizedString. But I get the following exception:
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'org.example.LocalizedString' as a subtype of [simple type, class org.example.LocalizedString]: known type ids = [LocalizedString] (for POJO property 'name')
The LocalizedString class looks like this:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, property="type")
#Value
public class LocalizedString {
private Map<Language, String> strings;
...
}
In the Avro schema file, I typed the name field as follows:
{
"name": "name",
"type": {
"type": "record",
"namespace": "org.example",
"name": "LocalizedString",
"fields": [
{
"name": "type",
"type": "string"
},
{
"name": "strings",
"type": ["null", {
"type": "map",
"values": "string"
}]
}
]
}
}
If I leave out the #JsonTypeInfo annotation, then it works. However, I'm using it for Json serialization elsewhere, so I'd love to keep it.
What is the specific problem for Avro here?
And maybe it's also problematic that the key of the strings map is not a string?
I have created one Model class(Product),it contains two feilds name and value.
I would like to give swagger default values for Product model. I was able to do it with the help of #ApiModelProperty annotation and it's working fine.
But my actual requirement is to give multiple values for same object something like this
{
"products": [
{
"name": "X",
"value": "100"
},
{
"name": "Y",
"value": "100"
},
{
"name": "Z",
"value": "100"
},
{
"name": "A",
"value": "01"
}
]
}
this default model I am trying to create for HTTP POST request how can I achieve this with swagger2 and springboot
You should mark the filed with datatype="List"
public class MyClass {
....
#ApiModelProperty(datatype="List", example = "'['{'name': 'X','value': '100'}']")
private List< Product> products;
....
}
Edit
public class MyClass {
....
#ApiModelProperty(value = "{\n'name':'X',\n'value':'100'\n},\n{\n'name':'Y',\n'value':'100'\n}")
private List< Product> products;
....
}
I am trying to generate a JSON schema using POJOs with deep inheritance structure.
Using jackson-module-jsonSchema library I am able to generate a schema.
Given a simplified Java example:
public interface I {...}
public class A implements I {
public int varA;
}
public class B implements I {
public int varB;
}
public class C {
public I varC;
}
Below is my code to generate the schema:
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.module.jsonSchema.*
// ...
ObjectMapper mapper = new ObjectMapper();
SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
mapper.acceptJsonFormatVisitor(mapper.constructType(C.class), visitor);
JsonSchema schema = visitor.finalSchema();
String outputSchemaJson = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(schema);
Actual Json Schema:
{
"type" : "object",
"id" : "urn:jsonschema:com:mycompany:GenerateSchemas:C",
"properties" : {
"varC" : {
"type" : "any"
}
}
}
Desired Json Schema:
{
"definitions": {
"A": {
"type" : "object",
"id" : "urn:jsonschema:com:mycompany:GenerateSchemas:A",
"properties" : {
"varA" : {
"type" : "integer"
}
}
},
"B": {
"type" : "object",
"id" : "urn:jsonschema:com:mycompany:GenerateSchemas:B",
"properties" : {
"varB" : {
"type" : "integer"
}
}
}
},
"type" : "object",
"id" : "urn:jsonschema:com:mycompany:GenerateSchemas:C",
"properties" : {
"varC" : {
"type" : "object",
"oneOf": [
{ "$ref": "urn:jsonschema:com:mycompany:GenerateSchemas:A" },
{ "$ref": "urn:jsonschema:com:mycompany:GenerateSchemas:B" }
]
}
}
}
I have tried overriding core classes from Json Schema library. This answer from stack overflow was helpful to generate a schema with references.
Now I am trying to understand what I need to override such that I can use reflection to get all inheriting-classes of an interface and add oneOf references to it.
I was finally able to figure out which classes I needed to override.
Notes:
Java does not support dynamically finding sub-classes via reflection through a simple out-of-box api. A workaround was to annotate classes with #JsonSubType which I was able to extract at run-time.
In version 2.9.8 of the json-module-schema library (where revision 1 of solution is written), there is no support yet for object definitions. Just to get the work done, I had to override a few extra classes to make this possible.
definitions need to be defined in the json schema only once at root level because there can be cases of recursive references.
With updated POJO code:
#JsonSubTypes({
#JsonSubTypes.Type(name = "A", value = A.class),
#JsonSubTypes.Type(name = "B", value = B.class)
})
public interface I {}
public class A implements I {
public int varA;
}
public class B implements I {
public int varB;
}
public class C {
public I varC;
}
The desired json schema output is produced successfully.
Given the following code: https://gist.github.com/rrmistry/2246c959d1c9cc45894ecf55305c61fd, I imported GenerateSchema class to make schema generation code more simplified:
public void generate() throws Exception {
generateSchemasFromJavaSubTypes(C.class);
}
private void generateSchemasFromJavaSubTypes(Class<?> classToGenerate) throws Exception {
JsonSchema schema = GenerateSchemas.generateSchemaFromJavaClass(classToGenerate);
ObjectMapper mapper = new ObjectMapper();
String jsonSchemaStr = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(schema);
}
GitHub issue has been created to request native support: https://github.com/FasterXML/jackson-module-jsonSchema/issues/135
I'm implementing a company internal REST service using spring boot 1.5.2 with Spring Data JPA and Data Rest.
Problem
I'm looking for an efficient way to serialize objects as strings when exposing certain domain models using Spring Data Rest-Repositories.
Context
My domain models all extend from BaseEntity which looks like this:
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity extends AbstractAuditable<User, Long> implements Serializable {
#Version
private Long version;
}
With this, each domain model has the properties createdBy, createDate, lastModifiedBy and lastModifiedDate exposed as shown in this example entity:
public class TestEntity extends BaseEntity { private String name; }
The corresponding JSON output looks like this:
{
"createdBy":
{
"name": "testEM",
"contactInfo":
{
"title": null,
"givenName": "GivenName",
"surName": "Surname",
"mail": "test#test.mail.de"
},
"function": "EMPLOYEE",
"department":
{
"name": "mydep"
}
},
"createdDate": "2017-06-12T11:49:17.013Z",
"lastModifiedBy":
{
<same representation as "createdBy">
},
"lastModifiedDate": "2017-06-14T11:27:32.370Z",
"name": "Hello,Name!",
"new": false,
"_links":
{
"self":
{
"href": "http://localhost:8080/testres/1"
},
"testEntity":
{
"href": "http://localhost:8080/testres/1{?projection}",
"templated": true
}
}
}
What I want
Now I'd like to achieve a shorter representation of createdBy and lastModfifiedBy so that these entries don't contain the User object. Instead only the name (from User.getName()) should be displayed:
{
"createdBy": "testEM",
"createdDate": "2017-06-12T11:49:17.013Z",
"lastModifiedBy": "testEM",
"lastModifiedDate": "2017-06-12T11:49:17.013Z",
... // other properties
}
What is the best way to achieve this?
I've tried:
using #JsonIdentityInfo on the User entity -- This one didn't have any effect at all
registering custom (de)serializers for the User entity via #Bean Jackson2ObjectMapperBuilderCustomizer customizer() {...} -- Rendered { "createdBy": { "content": "testEM"}}
annotating the overridden method public User getCreatedBy() in my BaseEntity class with #JsonSerialize(using= UserJsonSerializer.class) -- this one throws an exception
{
"timestamp": 1497515751192,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.http.converter.HttpMessageNotWritableException",
"message": "Could not write content: Can not override serializer; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not override serializer",
"path": "/testres/1"
}
I've also read about using #JsonView, however, it didn't become clear to me how to enable these for the given use case
Update
I've created some projections, which is the out-of-the-box supported way to reduce output. See this Gist for the code I've written.
With these in place, and the projections set as excerpts, the list of entries is displayed fine. However, when you request a specific resource like localhost:8080/testRepo/1 you get the unprojected output. I know that Spring won't apply projections to specific entities per default. So we'd have to apply the request parameter ?=projection=testProjection to each request.
Since this is doable (because the app won't be public) it may be okay, but for others it may not. So the questions still stands, how can we alter the audit info in an efficient way for each resource?
Update 2
I've read again the Spring Data REST Documentation and stumbled upon this paragraph:
There is another route. If the Address domain object does not have it’s own repository definition, Spring Data REST will inline the data fields right inside the Person resource.
So you have to expose an UserRepository when the auditor is of type User.
Coincidently, this is the exact behaviour which I experienced when creating a MWE (minimal working example, can't upload to github, since I'm behind a proxy :( ).
So, with a #RepositoryRestResource UserRepository extends JpaRepository<User, Long> publicly exposed, Spring generates this JSON:
{
"createdDate": "2017-06-12T11:49:17.013Z",
"lastModifiedDate": "2017-06-14T11:27:32.370Z",
"name": "Hello,EM!",
"new": false,
"_links":
{
"self":
{
"href": "http://localhost:8080/testRepo/1"
},
"testEntity":
{
"href": "http://localhost:8080/testRepo/1{?projection}",
"templated": true
},
"lastModifiedBy":
{
"href": "http://localhost:8080/testRepo/1/lastModifiedBy"
},
"createdBy":
{
"href": "http://localhost:8080/testRepo/1/createdBy"
}
}
}
This behaviour is acceptable for me, so consider this question solved.
If anyone has additional input feel free to post!
Any help here is much appreciated!
This isn't a solution for my asked question, but it is an acceptable compromise for me and the company.
Quick solution:
When you expose an RestRepository<User> in your API and your auditor is of the same type User, Spring will generate HAL-links to createdBy and lastModifiedBy. Both audit dates will be inlined still since they are simple strings (due to the JodaTime conversion).
Example code:
// resolves auditor from SecurityContext
public class AuditorAwareImpl implements AuditorAware<User> {
#Override
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof WrappedUser) {
WrappedUser principal = (WrappedUser)authentication.getPrincipal();
return principal.getUser();
}
throw new IllegalStateException("No current auditor available!");
}
}
Expose the UserRepository:
//exported is true by default
#RepositoryRestResource(exported = true)
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByName(String loginName);
}
Create AuditEntity from which all other domain objects inherit:
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity extends AbstractAuditable<User, Long> implements Serializable {
#javax.persistence.Version
private Long version;
}
Expose your domain models:
#Entity
public class Project extends BaseEntity {
private String project_name;
// other properties
}
#RepositoryRestResource
public interface ProjectRepo extends JpaRepository<User, Long> {}
This will generate following JSON for /projects/{id}:
{
"createdDate": "2017-06-12T11:49:17.013Z",
"lastModifiedDate": "2017-06-14T11:27:32.370Z",
"project_name": "MyExampleProjectName",
"new": false,
"_links":
{
"self":
{
"href": "http://localhost:8080/projects/1"
},
"project":
{
"href": "http://localhost:8080/projects/1{?projection}",
"templated": true
},
"lastModifiedBy":
{
"href": "http://localhost:8080/projects/1/lastModifiedBy"
},
"createdBy":
{
"href": "http://localhost:8080/projects/1/createdBy"
}
}
}
I want to create a JsonSchema file from my java classes using latest Jackson libraries .
When I have a java class like the following:
class MyClass {
String status;
}
how do I need to annotate the field so that the schema output would look like this:
{
...
"status": {
"description": "status",
"type": [ "string", "null" ],
"maxLength":12
}, ...
}
?
Cheers