Jackson FasterXML : How does Spring know how to json object? - json

I am currently following this tutorial about Spring MVC and REST service. It is a simple hello world project.
There is something I don't understand. In the pom.xml we set jackson dependency but it is never configurated. How does Spring know how to json object ?
Here is samples of code :
pom xml (dependencies) :
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.library}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>${jackson.library}</version>
</dependency>
</dependencies>
AppConfiguration :
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "package.to.scan")
public class AppConfiguration {
}
AppInitializer :
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfiguration.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
AppController :
#RestController
public class AppController {
#RequestMapping("/")
public String welcome() {
return "Welcome to RestTemplate Example.";
}
#RequestMapping("/hello/{player}")
public Message message(#PathVariable String player) {
Message msg = new Message(player, "Hello " + player);
return msg;
}
}
Model :
public class Message {
String name;
String text;
public Message(){
}
public Message(String name, String text) {
this.name = name;
this.text = text;
}
public String getName() {
return name;
}
public String getText() {
return text;
}
}

Spring has a default set of HttpMessageConverters, one of them being MappingJackson2HttpMessageConverter, which is used if the content type is json and if Jackson is available in the classpath.

Related

Don't work convert type boolean from Json with Srping Boot 2.x

I use Spring Boot 2.x and maven
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
json, That is sending.
{
"name": "fan",
"isCar": true,
"year": 2020,
"character": "W",
"cost": 10,
"status": "OPEN"
}
a part of DTO
public abstract class DifferentTypesDtoFour extends DifferentTypesDtoTwo {
private Integer year;
private boolean isCar;
public DifferentTypesDtoFour() {
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public boolean isCar() {
return isCar;
}
public void setCar(boolean car) {
isCar = car;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
DifferentTypesDtoFour that = (DifferentTypesDtoFour) o;
return isCar == that.isCar &&
Objects.equals(year, that.year);
}
#Override
public int hashCode() {
return Objects.hash(super.hashCode(), year, isCar);
}
#Override
public String toString() {
return super.toString() + "\n" + "DifferentTypesDtoFour{" +
"year=" + year +
", isCar=" + isCar +
'}';
}
}
I can 't understand the origin of this problem.
When the request comes to the rest controller , the dto receives the converted data.
Almost data is converted the correct, but the field
isCar = false...
Why ?
Does anyone have any ideas how to fix this?
Update
I used the following:
public abstract class DifferentTypesDtoFour extends DifferentTypesDtoTwo {
private Integer year;
private boolean isCar;
public DifferentTypesDtoFour() {
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
#JsonProperty(value = "isCar")
public boolean isCar() {
return isCar;
}
#JsonProperty(value = "isCar")
public void setCar(boolean car) {
isCar = car;
}
...
}
The type boolean saved indeed, but when the data is back to rest-controller, the client receives a response and the type boolean again don't transform correct.
The problem is with your setter of isCar field - the setter method's name should be setIsCar instead of setCar so that the request json's isCar value can be mapped into your model's isCar property,
public void setIsCar(boolean isCar) {
this.isCar = isCar;
}
Edit 1...
As #Gimby mentioned that your getter name should be isIsCar, it will not affect while getting HTTP request but it will eventually look unmatched getter for that field - and you may consider re-naming your variable to avoid such typo issues.
Edit 2... One of the ways to get rid of the issue is using #JsonProperty without renaming methods as mentioned by #GabLeg,
#JsonProperty(value="isCar")
public void setCar(boolean car) {
this.isCar = car;
}

Spring REST Controller returns empty JSON. Iterable data structure. Why?

I realize a very similar question was asked and closed because it wasn't specific enough and didn't specify outcomes. Closed Off Topic
Problem: JSON being returned from REST controller is empty. Verified data exists and is in the Iterable.
Expected Outcome: A JSON Array containing objects would be returned.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.codeexcursion</groupId>
<organization>
<name>Chris Lynch</name>
</organization>
<version>1.00.000</version>
<artifactId>work-tracking</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<name>Work Tracking</name>
<inceptionYear>2017</inceptionYear>
<developers>
<developer>
<id />
<name>Chris Lynch</name>
<email>chrislynch42#yahoo.com</email>
<timezone>-4</timezone>
<roles>
<role>Chief cook and bottle washer.</role>
</roles>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.5.10.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.5.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
<version>1.5.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.13.1.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.codeexcursion.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Entity
//Package and import Omitted
#Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long parentId;
private String title;
private String description;
protected Category() {
}
public Category(final String title, String description) {
this(0L, title, description);
}
public Category(Long parentId, final String title, String description) {
if (parentId == null) {
parentId = 0L;
}
if (title == null || title.trim().isEmpty()) {
throw new IllegalArgumentException("Title may not be null or empty.");
}
if (description == null) {
description = "";
}
this.parentId = parentId;
this.title = title;
this.description = description;
}
#Override
public String toString() {
return "id = " + id + "; parentId=" + parentId + "; title=" + title + "; description=" + description;
}
}
Resource
//Package and import Omitted
#Repository
public interface CategoryCRUD extends CrudRepository<Category, Long> {
List<Category> findByTitle(String title);
}
Rest Controller
//Package and import Omitted
#RestController
#RequestMapping("/categories")
public class CategoryController {
#Autowired
private CategoryCRUD categoryCRUD;
#RequestMapping(value = "", method = RequestMethod.GET)
public #ResponseBody Iterable<Category> list() {
System.out.println("findAll");
categoryCRUD.findAll().forEach((category) -> {
System.out.println("category=" + category);
});
return categoryCRUD.findAll();
}
private List<Category> findAll() {
final Iterable<Category> data = categoryCRUD.findAll();
List<Category> returnList = new ArrayList<>();
data.forEach(returnList::add);
return returnList;
}
}
I found an answer that was hinted on the closed post but wasn't detailed. I needed to add getters to my entity. I expected JPA/Spring to automagically add the getters and setters. The below fixed my problem.
Entity
//Package and imports omitted
#Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long parentId;
private String title;
private String description;
protected Category() {
}
public Category(final String title, String description) {
this(0L, title, description);
}
public Category(Long parentId, final String title, String description) {
if (parentId == null) {
parentId = 0L;
}
if (title == null || title.trim().isEmpty()) {
throw new IllegalArgumentException("Title may not be null or empty.");
}
if (description == null) {
description = "";
}
this.parentId = parentId;
this.title = title;
this.description = description;
}
#Override
public String toString() {
return "id = " + id + "; parentId=" + parentId + "; title=" + title + "; description=" + description;
}
public Long getId() {
return id;
}
public Long getParentId() {
return parentId;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
}
Better answers are welcome.
In my case, the getters to the fields of the entity were not public.
Making them public fixed the issue for me.
You have to include the lombok dependency in your pom.xml file and you have to setup the lombok jar in the IDE you are using (Can be Intellij or Eclipse). if you want to use the annotations #Data, it automatically generates the getters, setters and toString() method inside a Java Bean or Pojo class.
You can use #Getter, #Setter, #AllArgsConstructor, #NoArgsConstructor javadoc annotation from lombok will generate the getters and setters and constructors for your fields.
Please have a look at this http://www.baeldung.com/intro-to-project-lombok for more information.
Thanks!

Propagating foreign key with Spring Data

I am developing an Spring Boot app based on Spring Data Rest(which uses Hibernate underneath) and mySQL database. This app is incapable to populate the foreign keys of the referenced entries(because I expect Hibernate does it for me).
Entities:
#Entity
public class Producto {
private Integer id;
private String nombre;
private List<Formato> listaFormatos;
public Producto() {
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
#OneToMany(mappedBy = "producto", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public List<Formato> getListaFormatos() {
return listaFormatos;
}
public void setListaFormatos(List<Formato> listaFormatos) {
this.listaFormatos = listaFormatos;
}
}
#Entity
public class Formato {
private Integer id;
private Integer cantidad;
private String unidadMedida;
private Producto producto;
public Formato() {
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToOne
#JoinColumn(name = "producto_id", referencedColumnName = "id")
public Producto getProducto() {
return producto;
}
public void setProducto(Producto producto) {
this.producto = producto;
}
public Integer getCantidad() {
return cantidad;
}
public void setCantidad(Integer cantidad) {
this.cantidad = cantidad;
}
public String getUnidadMedida() {
return unidadMedida;
}
public void setUnidadMedida(String unidadMedida) {
this.unidadMedida = unidadMedida;
}
}
Repository:
public interface ProductoRepository extends CrudRepository<Producto, Integer> {
}
application.properties
spring.datasource.url = jdbc:mysql://localhost:3306/x1
spring.datasource.username = x2
spring.datasource.password = x3
spring.jpa.show-sql=true
spring.jpa.database=mysql
spring.jpa.hibernate.ddl-auto=create-drop
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
Let's say that the request I am sending in JSON format looks like this:
{"nombre": "x",
"listaFormatos": [
{"cantidad": 1,
"unidadMedida":"kg"},
{"cantidad": 2,
"unidadMedida":"g"}
]
}
The output of all this is that I persist an 'producto' and 2 'formato' without foreign key. Because of that, I believe I cannot bring a producto with its formats
Does anyone know why the foreign key is not being propagated?
Empty FKs usually happening when you are not setting the back references.
Consider the following:
Parent p = new Parent();
...
Child c = new Child();
...
p.setChild(c);
c.setParent(p); // this is the line you are probably missing
Of course, you can put this logic into the Parent#setChild method as well.

Spring Boot, Spring Security, MySQL - CustomUserDetailsService always results in the error "Invalid Username or Password"

The Problem i have resulted out of this tutorial.
My Problem is that i always run in the issue that my user is read out of the database but i don't get authenticated. On the view it always shows the error message "Invalid Username or Password". My Console-output shows no errors. When i debugged through my authentication process there were no unclear behaviours where the error could come from.
In my pom.xml i used the following dependencies.
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
My CustomUserDetailsService.java looks like this.
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
private final UserRolesRepository userRolesRepository;
#Autowired
public CustomUserDetailsService(UserRepository userRepository,
UserRolesRepository userRolesRepository) {
this.userRepository = userRepository;
this.userRolesRepository = userRolesRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("No user present with username " + username);
}
else {
List<String> userRoles = userRolesRepository.findRoleByUserName(username);
return new CustomUserDetails(user, userRoles);
}
}
}
CustomUserDetails.java
public class CustomUserDetails extends User implements UserDetails {
private static final long serialVersionUID = 1L;
private List<String> userRoles;
public CustomUserDetails(User user, List<String> userRoles) {
super(user);
this.userRoles = userRoles;
}
#Override
public Collection< ? extends GrantedAuthority> getAuthorities() {
String roles = StringUtils.collectionToCommaDelimitedString(userRoles);
return AuthorityUtils.commaSeparatedStringToAuthorityList(roles);
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
}
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home", "/images/**", "/css/**", "/js/**", "/loadEvents")
.permitAll().anyRequest().authenticated().and().formLogin().loginPage("/login")
.permitAll().and().logout().permitAll();
}
#Bean(name = "passwordEncoder")
public PasswordEncoder passwordencoder() {
return new BCryptPasswordEncoder();
}
}
As i mentioned before i don't think that the error occures on the database. I used the database scheme out of the tutorial. I also used User.java UserRole.java and the two repositories out of the tutorial.
My Application.java looks like this.
#SpringBootApplication
#EnableJpaRepositories(basePackages = "<package>")
#EntityScan(basePackages = "<package>")
public class Application {
public static void main(String[] args) throws Throwable {
SpringApplication.run(Application.class, args);
}
}
Update 1: Link to Git Project
https://github.com/pStuetz/Speeddating4SO/tree/master
You maybe have to Edit the src/main/resources/application.properties to support your database. I included the sql script which i used to create my database tables.
I've enabled trace logging in application.properties by adding this row
logging.level.=TRACE
and saw the error message
org.springframework.security.authentication.LockedException: User account is locked
in the console.
Your custom user details class de.dhbw.stuttgart.speeddating.userhandling.service.impl.CustomUserDetails returns "false" from methods "isAccountNonExpired", "isAccountNonLocked" and "isCredentialsNonExpired". I guess the methods should return the value of the property "enabled" from the class de.dhbw.stuttgart.speeddating.userhandling.service.User.
After changing all those "false" to "true", the login procedure started to work as expected.

REST, JAXB, jersey 2, JSON, field of type List<>

I have a RESTful service with some methods. Methods are annotated by #POST and #Consumes(MediaType.APPLICATION_JSON).
I have wrapper for request parameters:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class RequestWrapper {
#XmlElement
private SomeInfo someInfo = new SomeInfo();
#XmlElement
#XmlElementWrapper
private List<RequestParameter> requestParameters = new ArrayList<>();
}
public class SomeInfo {
public String field1;
public String field2;
}
public class RequestParameter {
public String key;
public String value;
}
I make request to my service. Body of the POST message:
{"someInfo":{"field1":"b","field2":"c"},"requestParameters":[{"key":"1","value":"2"},{"key":"3","value":"4"}]}
I see that someInfo values processed and accesible via RequestWrapper. But List<RequestParameter> requestParameters has zero length.
What should I do to messages been processed normally?
P.S.
I use Glassfish 4.0. Dependencies of the maven module:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
omg, I have found magic bullet
Delete #XmlAccessorType(XmlAccessType.FIELD)
Delete #XmlElementWrapper
make public getters/setters for each field
#XmlRootElement
public class RequestWrapper {
#XmlElement
private SomeInfo someInfo = new SomeInfo();
#XmlElement
private List<RequestParameter> requestParameters = new ArrayList<>();
// getters/setters for each field
}