Jackson LocalDate/Time deserialization - java

I configured jackson so that it gives me a smiple string representation if java.time.LocalDate and java.time.LocalDateTime. This works find in the serialization process, e.g when I get data on the REST api.
It doesn't work the other way round though. When I try to send data to the server and the JSON should be parsed to java objects this exception is thrown:
javax.ws.rs.client.ResponseProcessingException: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.LocalDateTime: no String-argument constructor/factory method to deserialize from String value ('2018-04-19T14:10:30.903')
After a few hours of research I managed to get it to work, but only with the attributes I annotated with #JsonDeserialize(using = LocalDateDeserializer.class) or #JsonDeserialize(using = LocalDateTimeDeserializer.class) respectively.
In my opinion it would be ideal, if I could define these mappings in one central place.
ObjectMapper configuration:
#Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
#Override
public ObjectMapper getContext(Class<?> aClass) {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return mapper;
}
}
I tried to add custom deserializers to the JavaTimeModule, but without success:
JavaTimeModule dateTimeModule = new JavaTimeModule();
dateTimeModule.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);
dateTimeModule.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
mapper.registerModule(dateTimeModule);
Long story short: Is there a way to define the mapping globally, so that I do not need these annotations on every field. Thanks!
EDIT:
Alright: I tested it with postman and without the annotations and it worked as expected. However, when I run the unit test (JerseyTest) it throws the mentioned exception. I register the ObjectMapperContextResolver to the test application, but probably I am missing something.
Sorry about not mentioning that I was in the unit tests.
TestClass:
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import javax.ws.rs.core.Application;
import java.time.LocalDate;
import java.time.LocalDateTime;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
public class PocRestTest extends JerseyTest {
private static PocService mockedPocService;
#Override
protected Application configure() {
mockedPocService = Mockito.mock(PocService.class);
ResourceConfig config = new ResourceConfig();
config.register(new PocRest(mockedPocService));
config.register(ObjectMapperContextResolver.class);
return config;
}
private Poc dto;
#Before
public void init() {
dto = new Poc();
dto.setId(1);
dto.setName("hi rest");
dto.setDate(LocalDate.now());
dto.setDateTime(LocalDateTime.now());
doReturn(dto).when(mockedPocService).getPocById(1);
}
#Test
public void test() {
Poc response = target("poc/1").request().get(Poc.class);
assertEquals(dto.getId(), response.getId());
assertEquals(dto.getName(), response.getName());
assertEquals(dto.getDate(), response.getDate());
assertEquals(dto.getDateTime(), response.getDateTime());
verify(mockedPocService).getPocById(1);
verifyNoMoreInteractions(mockedPocService);
}
}

You registered the ContextResolver with the server via the configure() method of the JerseyTest, but if you look at the exception, you'll see that is a client side exception (notice the client in the package name). So the problem is on the client side. What you are missing is that the deserialization also needs to happen on the client side from JSON to Poc, so you also need the ContextResolver registered on the client. To do that, you can override configureClient() on the JerseyTest and register the ContextResolver there.
#Override
public void configureClient(ClientConfig config) {
config.register(ObjectMapperContextResolver.class);
}

Related

Custom AOP spring boot annotation with ribbon client blocking api call with return "1"

I have really poor experience with ribbon/eureka so forgive me if this is a stupid question:
I have two different microservice both connected to a discovery server, the first one calls the second using a custom annotation that sends a request using rest template.
Custom annotation name is PreHasAuthority
Controller :
#PreHasAuthority(value="[0].getProject()+'.requirements.update'")
#PostMapping(CREATE_UPDATE_REQUIREMENT)
public ResponseEntity<?> createUpdateRequirement(#Valid #RequestBody RequirementDTO requirementDTO
, HttpServletRequest request, HttpServletResponse response) {
return requirementService.createUpdateRequirement(requirementDTO, request, response);
}
Annotation interface :
import java.lang.annotation.*;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface PreHasAuthority {
String value();
}
Annotation implementation:
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import netcomgroup.eu.service.AuthenticationService;
#Aspect
#Component
public class PreHasAuthorityServiceAspect {
#Autowired
private AuthenticationService authenticationService;
#Around(value = "#annotation(PreHasAuthority)")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
PreHasAuthority preHasAuthority = method.getAnnotation(PreHasAuthority.class);
Object[] args = joinPoint.getArgs();
String permission = preHasAuthority.value();
ExpressionParser elParser = new SpelExpressionParser();
Expression expression = elParser.parseExpression(permission);
String per = (String) expression.getValue(args);
String token =null;
for(Object o : args) {
if(o instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest)o;
token=request.getHeader("X-Auth");
break;
}
}
if(token==null) {
throw new IllegalArgumentException("Token not found");
}
boolean hasPerm = authenticationService.checkPermission(per,token);
if(!hasPerm)
throw new Exception("Not Authorized");
}
}
My Ribbon configuration
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
public class RibbonConfiguration {
#Autowired
IClientConfig config;
#Bean
public IRule ribbonRule(IClientConfig config) {
return new RoundRobinRule();
}
}
Eureka config in application properties
#Eureka config
eureka.client.serviceUrl.defaultZone= http://${registry.host:localhost}:${registry.port:8761}/eureka/
eureka.client.healthcheck.enabled= true
eureka.instance.leaseRenewalIntervalInSeconds= 10
eureka.instance.leaseExpirationDurationInSeconds= 10
by calling the api from postman request is sendend correctly to the second microservice and i'm certain the return is "true".
After that the request stops before entering the createUpdateRequirement method and returns '1' as postman body response. No error of sort is provided.
My guess is that the problem resides within the custom annotation, cause when i remove the annotation the api call works perfectly, but i cannot understand the problem as it seems all setted up correctly to me.
Your #Around advice never calls joinPoint.proceed(). Hence, the intercepted target method will never be executed.
The second problem is that your advice method returns void, i.e. it will never match any method returning another type such as the ResponseEntity<?> createUpdateRequirement(..) method.
Besides, around is a reserved keyword in native AspectJ syntax. Even though it might work in annotation-driven syntax, you ought to rename your advice method to something else like aroundAdvice or interceptPreHasAuthority - whatever.
Please do read an AspectJ or Spring AOP tutorial, especially the Spring manual's AOP chapter. 😉

Issue with stubbing/mocking the method with makes a DB call

I am having issue with Mocking a JDBC call using the MockitoJUnitRunner.
Somehow Mockito is not mocking the actual call even though I have below subbing line into the test class.
when(readOnlyJdbcTemplate.query(anyString(), any(Object[].class), any(int[].class), any(FeatureCollectionResponseExtractor.class))).thenReturn(actual);
Very similar mocking is working in another class for very similar type of method. The only difference between them is my other class does have 3 parameters instead of 4 parameters. Below is the code which is actually mocking successfully for different class.
when(readOnlyJdbcTemplate.query(anyString(), any(Object[].class), any(FeaturesResultExtractor.class))).thenReturn(actual);
Below is my actual code.
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.inject.Inject;
import javax.inject.Named;
import java.net.HttpURLConnection;
import java.sql.Types;
import static com.accounts.features.utils.Constants.INTERNAL_SERVER_ERROR;
#Profile
#Log
#Named("featureLibraryDao")
public class FeatureLibraryDaoImpl implements FeatureLibraryDao {
private static final Logger LOGGER = LogManager.getLogger(FeatureLibraryDaoImpl.class);
#Value("${feature.library.function.sql.query}")
private String sqlSelectQuery;
#Inject
#Named("readOnlyJdbcTemplate")
private JdbcTemplate readOnlyJdbcTemplate;
#Override
public FeatureCollectionDTO getFeaturesData(FeatureRequest request) {
try {
int[] argTypes = new int[] { Types.BIGINT, Types.VARCHAR, Types.SMALLINT};
return readOnlyJdbcTemplate.query(sqlSelectQuery, new Object[] {
Long.parseLong(request.getAccountId()), request.getRequestedFeatures(), request.getApplicationSuffix()
}, argTypes,
new FeatureCollectionResponseExtractor(request));
} catch (CustomException cbe) {
throw cbe;
} catch (Exception ex) {
LOGGER.error("getFeaturesData method failed with error message:{}", ex.getMessage(), ex);
CustomErrorCode error = new CustomErrorCode(INTERNAL_SERVER_ERROR);
error.setDeveloperText(ex.getMessage());
throw new CustomSystemException(error, HttpURLConnection.HTTP_INTERNAL_ERROR);
}
}
}
and below is my test class.
#RunWith(MockitoJUnitRunner.class)
public class FeatureLibraryDaoImplTest {
#InjectMocks
private FeatureLibraryDaoImpl dao;
#Mock
private JdbcTemplate readOnlyJdbcTemplate;
private List<String> features = Arrays.asList("excl_clsd_ind_only", "excl_chrgoff_ind_only", "excl_dsput_ind_only");
#Test
public void getFeaturesDataWhenSuccess() {
//given
FeatureRequest request = getFeatureRequest();
FeatureCollectionDTO actual = new FeatureCollectionDTO(features);
when(readOnlyJdbcTemplate.query(anyString(), any(Object[].class), any(int[].class), any(FeatureCollectionResponseExtractor.class))).thenReturn(actual);
//when
FeatureCollectionDTO dto = dao.getFeaturesData(request);
//then
assertThat(dto, notNullValue());
}
}
Any suggestion about what is wrong here? Is there any issue with any(int[].class) ?
I do see you are not passing the sql query sqlSelectQuery value during the test case, But during mock you specified anyString() so it must be some value but not null. Since you are using spring project, you can use ReflectionTestUtils to set the field value for object
#Before
public void setUp() {
ReflectionTestUtils.setField(dao, "sqlSelectQuery", "query");
}
Hey Guys thanks much for all your suggestions. So I found that Test code is perfectly fine. Some how #Value tag wasn't injecting the actual value the sqlSelectQuery in the main code file.
#Value("${feature.library.function.sql.query}") private String sqlSelectQuery;
Instead of that I changed code to private String sqlSelectQuery = "${feature.library.function.sql.query}" and all test cases are passing.
Somehow sqlSelectQuery was't getting the value and hence Mockito wasn't mocking the actual method call. I am yet reviewing why #value is not working as it should be.

Exposing endpoint URL in tests

Let's go straight to the problem:
I want to test a method that wants URL of Rest endpoint as a parameter. That method is using RestTemplate internally to send a request to that URL, so it needs to be full URL, for example http://localhost:8080/rest. Because of that I also have no way to mock RestTemplate.
I wanted to create simple #RestController but it seems that Spring is not creating endpoint when running tests.
I've tried creating MockMvc but it's not what I want. I have no way of getting the IP and port of MockMvc's created endpoint because no actual endpoint is created.
So, what I want is to make my #RestController accessible in tests by sending requests to URL, for example: http://localhost:8080/rest.
It's my first time creating a test like that, I will be grateful for your help. I was searching for an answer but I couldn't find a solution for my problem.
Edit:
Here's some code:
Unfortunately I can't post all the code but what I'm posting should be enough:
I have my endpoint like(names changed):
#RestController
public class EndpointController {
#RequestMapping(value = "/rest", method = RequestMethod.POST)
public List<Output> doSomething(#RequestBody Input[] requestList){
...
}
}
It's endpoint only for testing, it mimics the real endpoint. Then during my test I'm creating an object like:
new EndpointClient("http://localhost:8080/rest")
which has inside something like this:
ResponseEntity<Output[]> responseEntity = restTemplate.exchange(endpointURL,HttpMethod.POST, httpEntity, Output[].class);
Method having restTemplate request isn't called directly during testing(it's called by another method).
So I need to pass that URL to the Client object.
If you want to test your web application, checkout the Getting Started: Testing the Web Layer documentation.
Spring Boot is providing some useful annotations like #SpringBootTest, #AutoConfigureMockMvc, ...
See also the TestRestTemplate, which can be autowired to your WebMvcTest.
edit:
An example, copied from the documentation mentioned above:
package hello;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class HttpRequestTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
public void greetingShouldReturnDefaultMessage() throws Exception {
assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",
String.class)).contains("Hello World");
}
}
I found a solution for my problem.
I did it with WireMock: http://wiremock.org/
First I had to create Transformer for my request, so the response depends on request, something like:
public class MyTransformer extends ResponseTransformer {
#Override
public Response transform(final Request request,
final Response response,
final FileSource fileSource,
final Parameters parameters){
HttpHeaders headers = new HttpHeaders(new HttpHeader("Content-Type", "application/json"));
if(request.getUrl().contains("/rest")){
...
return Response.Builder.like(response).but().body(...).headers(headers).build();
} else
return Response.Builder.like(response).but().status(404).headers(headers).build();
}
#Override
public String getName() {
return "swapper";
}
}
In my test I needed to put
WireMockServer server = new WireMockServer(wireMockConfig().port(3665).extensions("package.name.endpoint.MyTransformer"));
server.stubFor(post("/rest"));
It created exactly what I wanted. Code above is extremely simple, probably not that good and needs work but it shows the basics.

Using #RequestLine with Feign

I have a working Feign interface defined as:
#FeignClient("content-link-service")
public interface ContentLinkServiceClient {
#RequestMapping(method = RequestMethod.GET, value = "/{trackid}/links")
List<Link> getLinksForTrack(#PathVariable("trackid") Long trackId);
}
If I change this to use #RequestLine
#FeignClient("content-link-service")
public interface ContentLinkServiceClient {
#RequestLine("GET /{trackid}/links")
List<Link> getLinksForTrack(#Param("trackid") Long trackId);
}
I get the exception
Caused by: java.lang.IllegalStateException: Method getLinksForTrack not annotated with HTTP method type (ex. GET, POST)
Any ideas why?
I wouldn't expect this to work.
#RequestLine is a core Feign annotation, but you are using the Spring Cloud #FeignClient which uses Spring MVC annotations.
Spring has created their own Feign Contract to allow you to use Spring's #RequestMapping annotations instead of Feigns. You can disable this behavior by including a bean of type feign.Contract.Default in your application context.
If you're using spring-boot (or anything using Java config), including this in an #Configuration class should re-enable Feign's annotations:
#Bean
public Contract useFeignAnnotations() {
return new Contract.Default();
}
The reason I got this error is that I used both #FeignClient and #RequestLine annotations in my FooClient interface.
Before a fix.
import org.springframework.cloud.openfeign.FeignClient; // #FeignClient
import feign.RequestLine; // #RequestLine
import org.springframework.web.bind.annotation.PathVariable;
#FeignClient("foo")
public interface FooClient {
#RequestLine("GET /api/v1/foos/{fooId}")
#Headers("Content-Type: application/json")
ResponseEntity getFooById(#PathVariable("fooId") Long fooId); // I mistakenly used #PathVariable annotation here, but this should be #Param
}
Then, I got this error.
Caused by: java.lang.IllegalStateException: Method FooClient#getFooById(Long) not annotated with HTTP method type (ex. GET, POST)
After a fix
// removed #FeignClient
// removed #PathVariable
import feign.Param; // Added
import feign.RequestLine; // #RequestLine
// removed #FeignClient("foo")
public interface FooClient {
#RequestLine("GET /api/v1/foos/{fooId}")
#Headers("Content-Type: application/json")
Foo getFooById(#Param("fooId") Long fooId); // used #Param
}
If you are interested in the configuration classes.
Please note that I tried to create Feign Clients Manually.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScans(value = {
#ComponentScan(basePackages = {
"com.example.app.service.web.client",
})
})
public class FeignConfig {
#Value(value = "${app.foo.service.client.url}")
protected String url; // http://localhost:8081/app
#Bean
public FooClient fooClient() {
FooClient fooClient = Feign.builder()
// .client(RibbonClient.create())
.client(new OkHttpClient())
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.logger(new Slf4jLogger(FooClient.class))
.logLevel(Logger.Level.FULL)
.target(FooClient.class, url);
return fooClient;
}
}
References
https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html
https://www.baeldung.com/intro-to-feign
https://www.baeldung.com/feign-requestline
https://stackoverflow.com/a/32488372/12898581
Your #RequestMapping value looks ok, but you're likely should consider slightly rewriting it:
#GetMapping(value = "/{trackid}/links")
List<Link> getLinksForTrack(#PathVariable(name = "trackid") Long trackId);
Btw I did not succeeded with getting #RequestLine to work due to same error as yours.
Also for #ReactiveFeignClients Contract.Default() yields to following errors:
java.lang.IllegalStateException: Method MyClient#doStuff(String,String) not annotated with HTTP method type (ex. GET, POST)
Warnings:
- Class MyClient has annotations [Component, ReactiveFeignClient, Metadata] that are not used by contract Default
- Method doStuff has an annotation GetMapping that is not used by contract Default
and should be fixed like:
var MyClient = WebReactiveFeign.builder()
.contract(new ReactiveContract(new SpringMvcContract()))
.target(MyClient, "http://example.com")

How to create a shortcut to existing annotation?

In my code I am using following annotation several times:
#JsonSerialize(using = classOf[CustomColorRGBASerializer])
To keep my code short and DRY, I would like to create a shortcut to this, something like:
class JsonSerializeARGB
extends #JsonSerialize(using = classOf[CustomColorRGBASerializer])
which I could then use as a new #JsonSerializeARGB annotation
I can use annotation, but I do not know how to define them, therefore my attempt certainly looks naive and obviously incorrect, but I hope it bears the meaning through.
I have read How do you define an #interface in Scala? and How to create annotations and get them in scala, but they did not help me much, as I do not want to create a brand new annotation, rather "subclass" existing annotation. Can this be done?
If there is no Scala solution, can something like this be done in Java? (The Jackson annotations I am working with are defined in Java anyway).
I'm afraid there is no way to subtype annotation with Java (and Scala) language mechanisms. I think that the only solution is to make a Scala macro with the annotation.
Macro annotations are available with Macro Paradise plugin for Scala compiler. Hopefully they 'll be included in Scala 2.13. To configure SBT for Macro Paradise you may want to follow this question. There is also a useful example of project making use of macro paradise.
I believe that this can be done better (especially DefDef matching), but macro similar to this one should solve your problem:
import scala.reflect.macros._
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
class JsonSerializeARGB extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro JsonSerializeARGBMacroImpl.impl
}
object JsonSerializeARGBMacroImpl extends JsonSerializeARGBMacro
class JsonSerializeARGBMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def modifiedDef(d: DefDef) = {
val (mods, name, tparams, paramss, tpt, body) = try {
val q"$mods def $name[..$tparams](...$paramss): $tpt = $body" = d
(mods, name, tparams, paramss, tpt, body)
} catch {
case _: MatchError => c.abort(c.enclosingPosition, "Failed to match...")
}
//TODO there is a problem with modifiers
c.Expr(q"""
#JsonSerialize(using = classOf[CustomColorRGBASerializer])
def $name[..$tparams](...$paramss): $tpt = $body
""")
}
annottees.map(_.tree) match {
case (d: DefDef) :: Nil => modifiedDef(d)
case _ => c.abort(c.enclosingPosition, "Invalid annottee.")
}
}
}
Looking at Java, there is no reasonable way to do this. Annotations cannot be extended in current Java versions, so the easiest approach fails. An other possiblity would be to use reflection to replace all occurrences of a JsonSerializeARGB with JsonSerialize, though this would only work at runtime, not at compile time. Yet the Java Reflection API only supports reading annotations, not adding them.
So there are two theoretical approaches:
Messing with the compiled byte code, but nobody can honestly want to do that.
Modifying Jackson (or any other library that reads the annotations) to recognize your custom JsonSerializeARGB annotation.
I’m not familiar with Scala, so I do not know whether there are other options available there. But I doubt that Scala provides methods to add or extends annotation that Java doesn’t.
Taking a different approach. Jackson supports programattically defining serializers. So you can define your own annotation and then use reflection to find all classes with your annotation and add the serializer mapping.
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule("MyModule", new Version(1, 0, 0, null))
// use reflections to find all classes with Annotation the
for (classWithAnnotation <- classesWithAnnotation) {
simpleModule.addSerializer(classWithAnnotation, new CustomColorRGBASerializer());
}
mapper.registerModule(simpleModule);
Here is the example I tried to get what you wanted to do with fasterXML library:
1. Create your own CustomSerializer
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.core.JsonProcessingException;
public class CustomSerializer extends JsonSerializer<CustomDTO> {
#Override
public void serialize(CustomDTO value, JsonGenerator gen,
com.fasterxml.jackson.databind.SerializerProvider serializers)
throws IOException,
JsonProcessingException {
gen.writeStartObject();
gen.writeStringField("AccentColor", value.getAccentColor());
gen.writeStringField("ButtonColor", value.getButtonColor());
gen.writeEndObject();
}
}
2. Create Annotation to use this CustomSerializer:
As of Scala 2.11 this needs to be done in Java, as in Scala it is currently not possible to define annotations with runtime retention.
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotationsInside
#JsonSerialize(using = CustomSerializer.class)
public #interface JsonSeriliazerCustom {}
3. Use this on CustomDTO or your class as follows:
#JsonSeriliazerCustom
public class CustomDTO {
private String buttonColor;
private String accentColor;
private String frontColor;
public String getButtonColor() {
return buttonColor;
}
public void setButtonColor(String buttonColor) {
this.buttonColor = buttonColor;
}
public String getAccentColor() {
return accentColor;
}
public void setAccentColor(String accentColor) {
this.accentColor = accentColor;
}
public String getFrontColor() {
return frontColor;
}
public void setFrontColor(String frontColor) {
this.frontColor = frontColor;
}
}
4. Write your main method like this:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import com.opera.oss.core.dto.CustomDTO;
public class TestJson {
public static void main(String[] args)
{
CustomDTO responseDTO = new CustomDTO();
responseDTO.setAccentColor("red");
responseDTO.setButtonColor("blue");
responseDTO.setFrontColor("yellow");
System.out.println("hey");
ObjectMapper om = new ObjectMapper();
VisibilityChecker<?> checker = om.getSerializationConfig().getDefaultVisibilityChecker();
om.setVisibilityChecker(checker.withFieldVisibility(JsonAutoDetect.Visibility.ANY));
try {
System.out.println(om.writer().writeValueAsString(responseDTO));
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Libraries used: fasterXML - 2.5.0 version - jackson-core, jackson-data-bind and jackson-annotations

Categories

Resources