Rest Api Data Representation - json

I hava an Spring Boot Rest Api
#RestController
public class BookController {
#Autowired
private BookRepository bookRepo;
#GetMapping(value = "/library/", produces ={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public List<Book> index(){
Iterable<Book> bookIterable = bookRepo.findAll();
List<Book> bookList = new ArrayList<>();
bookIterable.forEach(a->bookList.add(a));
return bookList;
}
My Homework is to add an additonal data representation so that when i put in the request i should can choose between which data representation i won't XML or JSON
Problem is
I get even json how can i change between XML and Json when i do a get Request to the Endpoint

To solve your problem you need to use the Accept header. more details
The Content Type header indicates the type of data that you pass in the request. more details
You need to make a request with the header, if you want to send and receive xml:
Accept: application/xml;
Content-Type: application/xml;
usefull link

Ok now i found it my self what you need to know in order to use an XML output is first add
to the pom.xml file following dependencies: Jackson XML Dataformat
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
Then i just have to Add Jackson Annotations to my Entity Class
#Entity
#JacksonXmlRootElement
public class Book {
public Book() {
}
#JacksonXmlProperty(isAttribute = true)
#Id
#GeneratedValue
private Integer id;
#JacksonXmlProperty(isAttribute = true)
private String title;
#JacksonXmlProperty(isAttribute = true)
private Integer numberOfCopies;
Thats it then i can Make a request with the Accept Header value application/xml

Related

Validating Post Json Data Spring Boot

Controller
#RequestMapping(value="/create", method=RequestMethod.POST, consumes={"application/json"})
public Alien addDetails(#RequestBody Alien alien){
return repo.save(alien);
}
Alien.java
#Entity
public class Alien{
#Id
private int id;
private String name;
private String planet;
Getter and setter
Now I want to validate the post json data before saving it to the database.
If any of the field is empty then the controller should return an error.
For example
{"id": 1, "name": "Alien1", "planet":"Mars" }
This is acceptable json data
But if there is any field is missing such as
{"name": "Alien1", "planet":"Mars" }
Then the controller should return an error and not creating the instance of Alien
I tried with #Valid #NotNull still the controller creates an empty instance of Alien and save to the database.
First of all, you should add spring boot validation dependency into your pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
And then add annotations like #NotBlank or #NotEmpty to verify if a value is assigned to that field or not. Those annotations should be on the top of your entity's attributes which you want to validate them.For example:
public class Alien{
#NotBlank(message="Name is mandatory")
private String name;
}
Finally, add #Valid annotation into your controller method like:
public Alien addDetails(#Valid #RequestBody Alien alien){
return repo.save(alien);
}
You should add #Validated annotation to your controller to make #Valid annotation do the work. You should also annotate fields in your Entity with constraints.
I.e., name could be #NotBlank.
And yes, make sure that you have imported:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Edit:
In Controller, you should not accept Entity as a body. Instead, you should create a DTO object and then map it to the Entity in the service. You should also put validation constraints on the DTO!

Why is my converted column marked by JpaAttributeTypeInspection with the error message "'Basic' attribute type should not be a map"?

I was trying to store some JSON as a string in a column via JPA and Spring and was following a baeldung tutorial. My code like this:
#Column
#Convert(converter = MyEntityExtentionConverter.class)
private Map<String, Object> myEntityExtention;
MyEntityExtentionConverter is an implementation of javax.persistence.AttributeConverter<Map<String, Object>, String> that converts the string back and forth using the Jackson ObjectMapper.
According to mentioned tutorial this should have been it, however now I get an Error that
'Basic' attribute type should not be a map
Theoretically I could disable it by adding #SuppressWarnings("JpaAttributeTypeInspection") to the annotations, but that feels like ignoring rather than solving the error. What am I doing wrong here?
You have to annotate prop "myEntityExtention" with #Type but is not possible to add both #Type and #Convert..
as you can see in this tutorial you have to define the json type on the top of your entity:
#Entity
#Table(name = "some_table_name")
#TypeDef(name = "json", typeClass = JsonStringType.class)
public class CustomEntity {
then add #Type annotation instead #Convert:
#Type( type = "json" )
private Map<String, Object> myEntityExtention;
be sure to add all the right dependencies/versions.
I.E. i'm using hibernate 5.4 so my dependencies are:
<!-- Hibernate ORM core version 5.4.21.Final (inherited from spring-boot 2.3.4)-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<!--for hibernate >= 5.2-->
<version>2.10.2</version>
</dependency>
</dependencies>
Look like this is an issue from IntelliJ IDEA:
https://youtrack.jetbrains.com/issue/IDEA-270687
We can use workaround by this way:
Using the #SuppressWarnings("JpaAttributeTypeInspection") annotation removes the warning.
That field is not meant to be persisted. Remove the #Column annotation and use #Transient.
You are supposed to persist it as a JSON which will be done in the customerAttributeJSON, when reading from the database the customerAttributes will be populated and you can use it with DTOs.
#Entity
#Table(name = "Customers")
public class Customer {
#Id
private int id;
private String firstName;
private String lastName;
private String customerAttributeJSON;
#Transient
#Convert(converter = HashMapConverter.class)
private Map<String, Object> customerAttributes;
}

Why does response.readEntity(Post.class) return null?

I'm trying to get JSON data from REST resource and automatically convert it to Java Object with JSON-to-Java binding. I use Jersey framework 2.21 with jersey-media-moxy module as JSON provider in my client application .
I cannot figure it out why I get null instead of proper Post object when I do this:
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://www.travelportland.com/wp-json");
Response response = webTarget.path("posts/9").request().get();
Post post = response.readEntity(Post.class); // => null
The Post class implementation looks like this (at this point I only want to take 'title' field from JSON):
#XmlRootElement
public class Post {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
All works fine when I try to get String:
String postStr = response.readEntity(String.class);
or if I try to get some other resource:
WebTarget webTarget = client.target("http://jsonplaceholder.typicode.com");
Response response = webTarget.path("posts/9").request().get();
Post post = response.readEntity(Post.class); // => com.example.Post#74e28667
It seems like this issue somehow related to structure or size of JSON data.
How can I solve this problem?
Seems to be an issue with MOXy. I am not sure what (maybe size, bad chars, I don't know). But I tested with Jackson, and it works fine. You might want to just make the switch Jackson if you can't figure it out with MOXy.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
</dependency>
You will need to set the class annotation to ignore unknown properties if you are not model all the keys from the JSON
#XmlRootElement
#JsonIgnoreProperties(ignoreUnknown = true)
public class Post {
Jackson will also recognize most JAXB annotations if you already using a bunch of them.
Also make sure you get rid of the the MOXy dependency of you will have to explicitly register JacksonFeature.class with the Client, to disable the MOXy provider.

How to receive JSON Messages in POST body in a JAX-RS Restful web service in CXF?

I'm trying to develop a REST service using Apache-CXF, on top of JAX-RS. For starters, I have a method called test that receives a String message and int value. I want the clients to be able to pass these parameters in a POST message body. I can't seem to achieve this.
Before I paste the code here, here are some details:
I'm using CXF without Spring
It's not a web app, so I don't have the WEB-INF folder with the web.xml
I test the service using SoapUI and Postman (Google Chrome application)
With the following code, I get WARNING: javax.ws.rs.BadRequestException: HTTP 400 Bad Request:
DemoService.java
#WebService(targetNamespace = "http://demoservice.com")
#Path("/demoService")
public interface DemoService {
#POST
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String test (String message, int value);
}
DemoServiceImpl.java
public class DemoServiceImpl implements DemoService {
#Override
public String test(String message, int value) {
return "test message: " + message + " value = : " + value;
}
}
DemoServer.java
public class DemoServer{
public static void main(String[] args) {
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
DemoService demoService = new DemoServiceImpl();
serverFactory.setServiceBean(demoService);
serverFactory.setAddress("http://localhost:9090");
serverFactory.create();
}
}
My POM.xml (minus the attributes in the root tag, everything's there)
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>demoService</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<cxf.version>3.0.0</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- Jetty is needed if you're are not using the CXFServlet -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description</artifactId>
<version>3.0.0-milestone1</version>
</dependency>
</dependencies>
</project>
Testing with {"message":"hello there!", "value":"50"} to the URL http://localhost:9090/demoService/test gave a HTTP 400 Bad Reuest.
Then I saw this question on S.O.: How to access parameters in a RESTful POST method and tried this:
added the following nested class in DemoServer.java:
#XmlRootElement
public static class TestRequest {
private String message;
private int value;
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public int getValue() { return value; }
public void setValue(int value) { this.value = value; }
}
I also modified the DemoService interface and the implementation to use this class as a parameter in the test method, although this is still ultimately not what I want to do. (just showing the implementation here, question's already getting long):
#Override
public String test(TestRequest testRequest) {
String message = testRequest.getMessage();
int value = testRequest.getValue();
return "test message: " + message + " value = : " + value;
}
And to fix this error that I got: SEVERE: No message body reader has been found for class DemoService$TestRequest, ContentType: application/json (in Postman I see error 415 - unsupported media type) I added the following dependencies (jettison and another thing) to the POM.xml:
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>2.6.0</version>
</dependency>
I tested the service using the following JSON message, in a HTTP POST request:
{"testRequest":{"message":"hello there!", "value":"50"}}
This works. Though this solution where I use a TestRequest class to encapsulate the parameters works, that's not the solution I'm looking for. I want to be able to pass the two parameters in a JSON message, without having to introduce this TestRequest class (explicitly).
Questions:
Would this be easier to implement using Jersey?
I don't have a web.xml nor a WEB-INF folder, so I can't configure CXF in a cxf.xml file can I? A lot of tutorials online seem ot use a lot of XML configuration, but I don't want to deploy a framework like TomEE or Spring or Glassfish just to do that.
Searching online for solutions, I came across Spring Boot. Would you recommend using that, perhaps? Would that make developing web services like this easier?
Also, how do I get it to return the value in JSON format (or is it not supposed to do that for Strings?)
My friend pointed me to this stack exchange question: JAX-RS Post multiple objects
and also the following documentation: http://cxf.apache.org/docs/jax-rs-and-jax-ws.html
which states:
public class CustomerService {
public void doIt(String a, String b) {...};
}
By default JAX-RS may not be able to handle such methods as it
requires that only a single parameter can be available in a signature
that is not annotated by one of the JAX-RS annotations like
#PathParam. So if a 'String a' parameter can be mapped to a #Path
template variable or one of the query segments then this signature
won't need to be changed :
#Path("/customers/{a}")
public class CustomerService {
public void doIt(#PathParam("a") String a, String b) {...};
}
So, to answer my question, NO, it cannot be done.

Java EE7 REST server no longer returning List<String> as JSON

The following example works in a Java EE6 (Glassfish3) project of mine but failed after I switched to Java EE7 (Glassfish4). The HTTP request returns "500 Internal Error" without any message in the Glassfish server log. The project was setup using NetBeans8 as Maven Web Project and has no special dependencies, beans.xml or other configuration.
#RequestScoped
#Path("generic")
public class GenericResource {
#GET
#Path("ping")
#Produces(APPLICATION_JSON)
public List<String> debugPing() {
return Arrays.asList("pong");
}
And then:
$ curl -v http://localhost:8080/mavenproject2/webresources/generic/ping
> GET /mavenproject2/webresources/generic/ping HTTP/1.1
...
< HTTP/1.1 500 Internal Server Error
As as I understand, all REST handling is done by the Jackson reference implementation and that Jackson uses Jersey as underlaying JSON library. One of the two is supposed to have some kind of provider for all basic data types. Only custom made classes need a self written ObjectMapper. Are these concepts still correct?
It took me some hours but I finally solved this question myself.
First fact is that the Glassfish4 JAX-RS implementation "Jersey" as switched its underlying JSON library from Jackson 1.x to Eclipselink MOXy. The latter seems not be able to convert Lists, Arrays and arbitrary POJOs to JSON out of the box. Therefore I tried to force JAX-RS to use Jackson 2.x and disable MOXy.
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
// This is Jackson 2.x, Jackson 1.x used org.codehaus.jackson!
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#ApplicationPath("rest")
public class RestConfig extends Application {
private final static Logger log = LoggerFactory.getLogger(RestConfig.class);
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<>();
log.info("Enabling custom Jackson JSON provider");
set.add(new JacksonJsonProvider() /* optionally add .configure(SerializationFeature.INDENT_OUTPUT, true) */);
return set;
}
#Override
public Map<String, Object> getProperties() {
Map<String, Object> map = new HashMap<>();
log.info("Disabling MOXy JSON provider");
map.put("jersey.config.disableMoxyJson.server", true);
return map;
}
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
// ... add your own REST enabled classes here ...
return resources;
}
}
My pom.xml contains:
<dependency>
<!-- REST (Jackson as JSON mapper) -->
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<!-- REST (Jackson LowerCaseWithUnderscoresStrategy etc.) -->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
Hope this helps someone!