Generate json schema from POJO - java

I need to generate json schema from my POJOs. The requirement is that every POJO must be exported as a separate file and the references inside the json schema must be handled appropriately. It means that the library should keep track of which POJO is exported to which file. I found this library: https://github.com/mbknor/mbknor-jackson-jsonSchema and it works fine but it seems (or at least i cannot find such option) that i can't accomplish the requirements without custom coding. Do you know any other library that supports this?

You can use Jackson to generate the JSON schema using the following maven dependencies
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jsonSchema</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
You can then generate the schema by writing something like this
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper);
Reflections reflections = new Reflections("my.pojo.model",new SubTypesScanner(false));
Set<Class<?>> pojos = reflections.getSubTypesOf(Object.class);
Map<String, String> schemaByClassNameMap = pojos.stream()
.collect(Collectors.toMap(Class::getSimpleName, pojo -> getSchema(mapper, schemaGen, pojo)));
schemaByClassNameMap.entrySet().forEach(schemaByClassNameEntry->writeToFile(schemaByClassNameEntry.getKey(),schemaByClassNameEntry.getValue()));
}
private static void writeToFile(String pojoClassName, String pojoJsonSchema) {
try {
Path path = Paths.get(pojoClassName + ".json");
Files.deleteIfExists(path);
byte[] strToBytes = pojoJsonSchema.getBytes();
Files.write(path, strToBytes);
}catch (Exception e){
throw new IllegalStateException(e);
}
}
private static String getSchema(ObjectMapper mapper,JsonSchemaGenerator schemaGenerator,Class clazz){
try {
JsonSchema schema = schemaGenerator.generateSchema(clazz);
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema);
}catch (Exception e){
throw new IllegalStateException(e);
}
}

Related

How to write a servlet program which consume both XML and JSON?

I am writing a servlet program, which aim to accept both xml and json, my request in json is this,
{"Symbol":["OLM","ASC"]}
and it is working well.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
Connection connection = null;
BufferedReader reader1 = request.getReader();
StringBuffer jb = new StringBuffer();
String line = null;
while ((line = reader1.readLine()) != null) {
jb.append(line);
}
String str = jb.toString();
JSONObject obj2 = null;
try {
obj2 = new JSONObject(str);
} catch (JSONException e1) {
e1.printStackTrace();
}
JSONArray array = null;
try {
array = (JSONArray) obj2.get("Symbol");
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
I know that it is working for json because of I am casting the obtained string(in my case str) to JSONObject, but if I want to accept XML also and obtain Symbol from it, how to change this code?
Thanks in advance
Iam updating my question,
ObjectMapper objectMapper = new ObjectMapper();
if(request.getHeader("content-type")=="application/json") {
System.out.println("json ");
Symbol symbolContainerFromJson = objectMapper.readValue(request.getReader(), Symbol.class);
System.out.println(symbolContainerFromJson.getSymbolName());
}
else if (request.getHeader("content-type")=="application/xml") {
System.out.println("xml");
Symbol symbolContainerFromXml = new XmlMapper().readValue(request.getReader(), Symbol.class);
System.out.println(symbolContainerFromXml.getSymbolName());
}
But it is not entering both the loops, kindly help
The most robust way to deserialize is by creating the data structure in the back-end and let a framework like Jackson do the heavy lifting.
So we first create our object representation that we expect in either XML or JSON. No magic, it's just a POJO. I add a JsonProperty annotation because you expect Symbol upper-case and I hate upper-case fields in Java.
public class SymbolContainer {
#JsonProperty("Symbol")
private List<String> symbol;
public List<String> getSymbol() {
return symbol;
}
}
Then I use a Jackson Object/Xml mapper to transform the content from the request body to an in-memory object.
if(contentTypeIsJson(request.getHeader("content-type"))) {
SymbolContainer symbolContainerFromJson = new ObjectMapper().readValue("{\"Symbol\":[\"OLM\",\"ASC\"]}", SymbolContainer.class);
System.out.println(symbolContainerFromJson.getSymbol()); // [OLM, ASC]
} else if (contentTypeIsXml(request.getHeader("content-type"))) {
SymbolContainer symbolContainerFromXml = new XmlMapper().readValue("<root>\n" +
" <Symbol>\n" +
" <element>OLM</element>\n" +
" <element>ASC</element>\n" +
" </Symbol>\n" +
"</root>", SymbolContainer.class);
System.out.println(symbolContainerFromXml.getSymbol()); // [OLM, ASC]
}
This using only these three Jackson dependencies
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.5</version>
</dependency>
Note that these ObjectMappers can be configured. If you are only interested in a part of the request (eg Symbol) and want to ignore the rest of the passed object, best to configure your ObjectMapper like so, which lets you ignore unmapped fields.
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

How to exclude lib in Swagger API documentation

I have used keycloak-admin-client in a jaxrs application, and use swagger to generate API documentation. Swagger generates documents for all methods in keycloak-admin-client. How can I exclude this library from documentation in swagger ?
Dependency:
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>7.0.0</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-servlet-initializer</artifactId>
<version>2.1.0</version>
</dependency>
by this solution assign only my package for scan
#ApplicationPath("/api")
public class ApplicationInitializer extends Application {
public ApplicationInitializer(#Context ServletConfig servletConfig) {
super();
OpenAPI oas = new OpenAPI();
try {
Set<String> resource = new HashSet<>();
resource.add("my.company.api.path");
SwaggerConfiguration oasConfig = new SwaggerConfiguration()
.openAPI(oas)
.prettyPrint(true)
.resourcePackages(resource);
OpenApiContext oac = new JaxrsOpenApiContextBuilder()
.servletConfig(servletConfig)
.application(this)
.openApiConfiguration(oasConfig)
.buildContext(true);
oac.read();
} catch (OpenApiConfigurationException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

I can't upload files with graphql-java

Im tried upload file with java graphql. I looked at a solution to this topic: How to upload files with graphql-java?
I'm using graphql-java version 11.0, graphql-spring-boot-starter version 5.0.2, graphql-java-kickstart version 7.5.0 .
public class PartDeserializer extends JsonDeserializer {
#Override
public Part deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return null;
}
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
SimpleModule module = new SimpleModule();
module.addDeserializer(Part.class, new PartDeserializer());
objectMapper.registerModule(module);
return objectMapper;
}
}
#Configuration
public class GraphqlConfig {
#Bean
public GraphQLScalarType uploadScalarDefine() {
return ApolloScalars.Upload;
}
}
public Boolean testMultiFilesUpload(List<Part> parts, DataFetchingEnvironment env) {
// get file parts from DataFetchingEnvironment, the parts parameter is not use
List<Part> attachmentParts = env.getArgument("files");
int i = 1;
for (Part part : attachmentParts) {
String uploadName = "copy" + i;
try {
part.write("your path:" + uploadName);
} catch (IOException e) {
e.printStackTrace();
}
i++;
}
return true;
}
scalar Upload
testMultiFilesUpload(files: [Upload!]!): Boolean
My query from-data in Postman like that
operations
{ "query": "mutation($files: [Upload!]!) {testMultiFilesUpload(files:$files)}", "variables": {"files": [null,null] } }
map
{ "file0": ["variables.files.0"] , "file1":["variables.files.1"]}
file0
0.jpeg
file1
1.jpeg
this is server response
INFO 11663 --- [0.1-1100-exec-7] g.servlet.AbstractGraphQLHttpServlet : Bad POST multipart request: no part named "graphql" or "query"
what I'm doing wrong?
you can try this dependencies :
<properties>
<graphql-java.version>13.0</graphql-java.version>
<graphql-java-kickstart-springboot.version>5.10.0</graphql-java-kickstart-springboot.version>
<graphql-java-kickstart-tools.version>5.6.1</graphql-java-kickstart-tools.version>
<graphql-java-kickstart-servlet.version>8.0.0</graphql-java-kickstart-servlet.version>
</properties>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>5.9.0</version>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>5.6.1</version>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>5.6.0</version>
</dependency>
But there is a problem with graphql file upload,we can't delete the temp file that generate by graphql, because it always be used by graphql and the file stream didn't closed.
I suggest you use Apollo
https://github.com/apollographql/apollo-android
It uses RxJava Integration, Retrofit, Subscriptions and support for AutoValue. This will make your work easier as there are no straightforward ways in building Queries & Parsing responses for GraphQL.

How to test if JSON path does not include a specific element, or if the element is present it is null?

I have been writing some simple unit testing routines for a simple spring web application. When I add #JsonIgnore annotation on a getter method of a resource, the resulting json object does not include the corresponding json element. So when my unit test routine tries to test if this is null (which is the expected behavior for my case, I don't want the password to be available in json object), test routine runs into an exception:
java.lang.AssertionError: No value for JSON path: $.password, exception: No results for path: $['password']
This is the unit test method I wrote, testing the 'password' field with is(nullValue()) method:
#Test
public void getUserThatExists() throws Exception {
User user = new User();
user.setId(1L);
user.setUsername("zobayer");
user.setPassword("123456");
when(userService.getUserById(1L)).thenReturn(user);
mockMvc.perform(get("/users/1"))
.andExpect(jsonPath("$.username", is(user.getUsername())))
.andExpect(jsonPath("$.password", is(nullValue())))
.andExpect(jsonPath("$.links[*].href", hasItem(endsWith("/users/1"))))
.andExpect(status().isOk())
.andDo(print());
}
I have also tried it with jsonPath().exists() which gets a similar exception stating that the path doesn't exist. I am sharing some more code snippets so that the whole situation becomes more readable.
The controller method I am testing looks something like this:
#RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(#PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = userService.getUserById(userId);
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
I am using spring hateos resource assembler for converting entity to resource objects and this is my resource class:
public class UserResource extends ResourceSupport {
private Long userId;
private String username;
private String password;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#JsonIgnore
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
I understand why this is getting an exception, also in a way, the test is successful that it could not find the password field. But what I want to do is, run this test to ensure that the field is not present, or if present, it contains null value. How can I achieve this?
There is a similar post in stack overflow:
Hamcrest with MockMvc: check that key exists but value may be null
In my case, the field may be non existent as well.
For the record, these are the versions of test packages I am using:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
[EDIT]
To be more precise, say, you have to write a test for an entity where you know some of the fields need to be null or empty or should not even exists, and you don't actually go through the code to see if there is a JsonIgnore added on top of the property. And you want your tests to pass, how can I do this.
Please feel free to tell me that this is not practical at all, but still would be nice to know.
[EDIT]
The above test succeeds with the following older json-path dependencies:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
[EDIT] Found a quickfix that works with latest version of jayway.jasonpath after reading the documentation of spring's json path matcher.
.andExpect(jsonPath("$.password").doesNotExist())
I had the same problem with the newer version. It looks to me that the doesNotExist() function will verify that the key is not in the result:
.andExpect(jsonPath("$.password").doesNotExist())
There is a difference between the property that is present, but having null value, and the property not being present at all.
If the test should fail only when there is a non-null value, use:
.andExpect(jsonPath("password").doesNotExist())
If the test should fail as soon as the property is present, even with a null value, use:
.andExpect(jsonPath("password").doesNotHaveJsonPath())
#JsonIgnore is behaving as expected, not producing the password in the json output, so how could you expect to test something that you are explicitly excluding from the output?
The line:
.andExpect(jsonPath("$.property", is("some value")));
or even a test that the property is null:
.andExpect(jsonPath("$.property").value(IsNull.nullValue()));
correspond to a json like:
{
...
"property": "some value",
...
}
where the important part is the left side, that is the existence of "property":
Instead, #JsonIgnore is not producing the porperty in the output at all, so you can't expect it not in the test nor in the production output.
If you don't want the property in the output, it's fine, but you can't expect it in test.
If you want it empty in output (both in prod and test) you want to create a static Mapper method in the middle that is not passing the value of the property to the json object:
Mapper.mapPersonToRest(User user) {//exclude the password}
and then your method would be:
#RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(#PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = Mapper.mapPersonToRest(userService.getUserById(userId));
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
At this point, if your expectations are for Mapper.mapPersonToRest to return a user with a null password, you can write a normal Unit test on this method.
P.S. Of course the password is crypted on the DB, right? ;)
doesNotHaveJsonPath for checking that it is not in json body
I wanted to reuse the same code I use for testing for the parameter being supplied, and for it missing, and this is what I came up with
#Test
void testEditionFoundInRequest() throws JsonProcessingException {
testEditionWithValue("myEdition");
}
#Test
void testEditionNotFoundInRequest() {
try {
testEditionWithValue(null);
throw new RuntimeException("Shouldn't pass");
} catch (AssertionError | JsonProcessingException e) {
var msg = e.getMessage();
assertTrue(msg.contains("No value at JSON path"));
}
}
void testEditionWithValue(String edition) {
var HOST ="fakeHost";
var restTemplate = new RestTemplate();
var myRestClientUsingRestTemplate = new MyRestClientUsingRestTemplate(HOST, restTemplate);
MockRestServiceServer mockServer;
ObjectMapper objectMapper = new ObjectMapper();
String id = "userId";
var mockResponse = "{}";
var request = new MyRequest.Builder(id).edition(null).build();
mockServer = MockRestServiceServer.bindTo(restTemplate).bufferContent().build();
mockServer
.expect(method(POST))
// THIS IS THE LINE I'd like to say "NOT" found
.andExpect(jsonPath("$.edition").value(edition))
.andRespond(withSuccess(mockResponse, APPLICATION_JSON));
var response = myRestClientUsingRestTemplate.makeRestCall(request);
} catch (AssertionError | JsonProcessingException e) {
var msg = e.getMessage();
assertTrue(msg.contains("No value at JSON path"));
}

How to serialize Object to JSON?

I need to serialize some objects to a JSON and send to a WebService. How can I do it using the org.json library? Or I'll have to use another one? Here is the class I need to serialize:
public class PontosUsuario {
public int idUsuario;
public String nomeUsuario;
public String CPF;
public String email;
public String sigla;
public String senha;
public String instituicao;
public ArrayList<Ponto> listaDePontos;
public PontosUsuario()
{
//criando a lista
listaDePontos = new ArrayList<Ponto>();
}
}
I only put the variables and the constructor of the class but it also have the getters and setters. So if anyone can help please
Easy way to do it without annotations is to use Gson library
Simple as that:
Gson gson = new Gson();
String json = gson.toJson(listaDePontos);
One can use the Jackson library as well.
Add Maven Dependency:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
Simply do this:
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString( serializableObject );
The quickest and easiest way I've found to Json-ify POJOs is to use the Gson library.
This blog post gives a quick overview of using the library.
You make the http request
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
inputStream = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 8);
StringBuilder sb = new StringBuilder();
You read the Buffer
String line = null;
while ((line = reader.readLine()) != null)
{
sb.append(line + "\n");
}
Log.d("Result", sb.toString());
result = sb.toString();
Create a JSONObject and pass the result string to the constructor:
JSONObject json = new JSONObject(result);
Parse the json results to your desired variables:
String usuario= json.getString("usuario");
int idperon = json.getInt("idperson");
String nombre = json.getString("nombre");
Do not forget to import:
import org.json.JSONObject;
GSON is easy to use and has relatively small memory footprint. If you loke to have even smaller footprint, you can grab:
https://github.com/ko5tik/jsonserializer
Which is tiny wrapper around stripped down GSON libraries for just POJOs
The "reference" Java implementation by Sean Leary is here on github. Make sure to have the latest version - different libraries pull in versions buggy old versions from 2009.
Java EE 7 has a JSON API in javax.json, see the Javadoc. From what I can tell, it doesn't have a simple method to marshall any object to JSON, you need to construct a JsonObject or a JsonArray.
import javax.json.*;
JsonObject value = Json.createObjectBuilder()
.add("firstName", "John")
.add("lastName", "Smith")
.add("age", 25)
.add("address", Json.createObjectBuilder()
.add("streetAddress", "21 2nd Street")
.add("city", "New York")
.add("state", "NY")
.add("postalCode", "10021"))
.add("phoneNumber", Json.createArrayBuilder()
.add(Json.createObjectBuilder()
.add("type", "home")
.add("number", "212 555-1234"))
.add(Json.createObjectBuilder()
.add("type", "fax")
.add("number", "646 555-4567")))
.build();
JsonWriter jsonWriter = Json.createWriter(...);
jsonWriter.writeObject(value);
jsonWriter.close();
But I assume the other libraries like GSON will have adapters to create objects implementing those interfaces.
After JAVAEE8 published , now you can use the new JAVAEE API JSON-B (JSR367)
Maven dependency :
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1</version>
</dependency>
Here is some code snapshot :
Jsonb jsonb = JsonbBuilder.create();
// Two important API : toJson fromJson
String result = jsonb.toJson(listaDePontos);
JSON-P is also updated to 1.1 and more easy to use. JSON-P 1.1 (JSR374)
Maven dependency :
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1</version>
</dependency>
Here is the runnable code snapshot :
String data = "{\"name\":\"Json\","
+ "\"age\": 29,"
+ " \"phoneNumber\": [10000,12000],"
+ "\"address\": \"test\"}";
JsonObject original = Json.createReader(new StringReader(data)).readObject();
/**getValue*/
JsonPointer pAge = Json.createPointer("/age");
JsonValue v = pAge.getValue(original);
System.out.println("age is " + v.toString());
JsonPointer pPhone = Json.createPointer("/phoneNumber/1");
System.out.println("phoneNumber 2 is " + pPhone.getValue(original).toString());

Categories