I'm using the objectMapper to first serialise and deserialise an object.
I'm serialising the object here:
byte[] data = objectMapper.writeValueAsBytes(service.getServiceInfo());
client.create().withMode(CreateMode.EPHEMERAL).forPath(service.getLeaderPath(), data);
The getServiceInfo is of type: ServiceInfo.class
Here is how I'm trying to deserialise the data:
byte[] data = client.getData().forPath(service.getLeaderPath());
T serviceInfo = objectMapper.readValue(data, typeServiceInfo);
Here T is of type ServiceInfo.class and typeServiceInfo is it's class variable Class<T>
This is the ServiceInfo.class:
#Data
public class ServiceInfo {
private String name;
public ServiceInfo(String name) {
this.name = name;
}
}
Now when I run my code, I obtain a MismatchedInputException
This is the error trace I obtained:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.nutanix.categories.beans.curator.ServiceInfo` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (byte[])"{"name":"2816c308-5277-4b23-bdd6-64d6f3513e16"}"; line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1429)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1059)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3266)
at com.nutanix.categories.curators.ServiceLeaderLatch.start(ServiceLeaderLatch.java:74)
What am I doing wrong here? Any help is deeply appreciated.
PS: Please mention if I have to submit additional information regarding my query in the comments
There are two ways to solve it:
Modify the ServiceInfo bean itself and remove the constructor. Although, it will require you to update all it's declarations.
#Data
public class ServiceInfo {
private String name;
}
Or, add #JsonCreator annotation to the bean
#Data
public class ServiceInfo {
private String name;
#JsonCreator
public ServiceInfo(#JsonProperty("name") String name) {
this.name = name;
}
}
If you don't like #JsonProperty annotation, you can customize ObjectMapper
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-paranamer</artifactId>
<version>${some-version}</version>
</dependency>
And then register the module:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModules(new ParanamerModule());
Another option is leave everything as it is and add a default constructor
#Data
public class ServiceInfo {
private String name;
public ServiceInfo() {
}
public ServiceInfo(String name) {
this.name = name;
}
}
Related
I have a product table, I have a second option table. I cannot manage to create my options for the product at the same time as I create the product. I tried to create the options individually by creating an option table and a category join table. When I send the options in json format it doesn't work. I get the bad request error and in the console:
JSON parse error: Cannot construct instance of
com.pastrycertified.cda.dto.OptionsDto (although at least one
Creator exists): no String-argument constructor/factory method to
deserialize from String value('pie'); nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
construct instance of com.pastrycertified.cda.dto.OptionsDto
(although at least one Creator exists): no String-argument
constructor/factory method to deserialize from String value
('pie') at [Source:
(org.springframework.util.StreamUtils$NonClosingInputStream); line: 2,
column: 19] (through reference chain:
java.util.LinkedHashMap["typeOption"])]
Thank you for help
data
{
"typeOption": "product",
"ingredients": {
"option1": "test",
"option2":"test1"
}
}
controller option
#RestController
#RequestMapping("/options")
#RequiredArgsConstructor
public class OptionsController {
private final OptionsService optionsService;
#PostMapping("/")
public void save(
#RequestBody Map<String, OptionsDto > options
) {
return ResponseEntity.ok(optionsService.save(options));
}
}
optionService
public interface OptionsService {
Options save(OptionsDto options);
}
optionServiceImpl
#Service
#RequiredArgsConstructor
public class OptionsServiceImpl implements OptionsService {
#Override
public Options save(OptionsDto options) {
Options option = OptionsDto.toEntity(options);
option.setTypeOption(option.getTypeOption());
option.setIngredients(option.getIngredients());
return option;
}
}
optionDto
#Getter
#Setter
#AllArgsConstructor
#Builder
public class OptionsDto {
private Integer id;
private String typeOption;
private String ingredients;
private String nameCategory;
private CategoryDto category;
public static OptionsDto fromEntity(Options options) {
return OptionsDto.builder()
.id(options.getId())
.typeOption(options.getTypeOption())
.ingredients(options.getIngredients())
.nameCategory(options.getCategory().getName())
.build();
}
public static Options toEntity(OptionsDto options) {
return Options.builder()
.id(options.getId())
.typeOption(options.getTypeOption())
.ingredients(options.getIngredients())
.build();
}
}
As Jens mentioned, you need a default constructor in the OptionDto class. Also, you must decide whether ingredients is a String or a Map.
In the controller, you are asking for a Map<> but what you pass in the JSON is not a map. Your controller must be asking for an OptionsDto and not a Map.
I am using SpringBoot 2.3.1-RELEASE and am trying to deserialize JSON string to a POJO containing list of objects but I keep running into this error:
Cannot construct instance of com.response.dto.RootDTO (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('Meta')
at [Source: (String)""Meta":[{"DimensionName":"Version","DimensionId":"3b4860b9-b215-4192-bd7a-a76f377fc465","DimensionType":"Regular","Alias":"C0","AttributeId":"211d5-d91f-40ec-9668-20e0da2ae7b3","AttributeName":"Version Name","AttributeKey":"VersionKey"; line: 1, column: 1]
This is what my JSON string looks like (but with escape chars in eclipse):
{"Meta":[{"DimensionName":"Version", "DimensionId":"3b4860b9-b215-4192-bd7a-a76f377fc465, "DimensionType":"Regular","Alias":"C0","AttributeId":"211b33d5-d91f-40ec-9668-20e0da2ae7b3","AttributeName":"Version Name","AttributeKey":"VersionKey"}]}.
Here is the class I want to deserialize it to:
#JsonIgnoreProperties(ignoreUnknown = true)
#Data
public class RootDTO
{
#JsonProperty("Meta")
private List<MetaDTO> Meta;
}
#JsonIgnoreProperties(ignoreUnknown = true)
#Data
public class MetaDTO
{
#JsonProperty("DimensionName")
private String DimensionName;
#JsonProperty("AttributeId")
private String AttributeId;
#JsonProperty("AttributeName")
private String AttributeName;
#JsonProperty("Name")
private String Name;
#JsonProperty("Alias")
private String Alias;
}
This is the code that blows up when trying to read the value:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.readValue(jsonFormattedString, RootDTO.class));
I only see this issue while running Junit (version : 4.12). I see jackson-databind-2.11.0, spring-test-5.2.7.RELEASE in the stack trace. However, I debug using a call from browser or postman it works fine. I am not sure why it is looking for the string Meta when I have specified it to be a list. What could be causing this issue? Any suggestions?
Edit: Turns out that the string which was being supplied to the ObjectMapper isn't the correct one. There is this line of code
String jsonFormattedString = responseEntity.getBody().substring(1, responseEntity.getBody().lastIndexOf("\"")).replaceAll("\\\\", ""); which makes my mocked string invalid. I'll need to figure out why we are doing this though.
Change the first letter's of variable to lowwer case. And remove the JsonProperty.
As below. And auto generate the setter and getter.
private String DimensionName;
private String attributeId;
private String attributeName;
private String name;
public void setName(String name){
this.name=name;
}
.........
.........
//All setter getter
Add #JsonGetter("Meta") each getter method.
For example as below.
#JsonGetter("Meta")
public List<Meta> getMeta(){
return meta;
}
I have a simple enum Days
public enum Days {
#JsonProperty("Monday")
MONDAY("Monday"),
#JsonProperty("Tuesday")
TUESDAY("Tuesday");
private String day;
Days(String day) {
this.day = day;
}
#JsonValue
public String getDay() {
return day;
}
}
and a class Event
public class Event {
private Days day;
private String name;
#JsonCreator
public Event(#JsonProperty("day") Days day,
#JsonProperty("name") String name) {
this.day = day;
this.name = name;
}
public Days getDay() {
return day;
}
public String getName() {
return name;
}
}
I am using Jackson 2.9, and this answer indicates that using #JsonProperty should be enough, however I struggle to deserialize this:
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Event event = new Event(Days.MONDAY, "Birthday");
String serialisedEvent = objectMapper.writeValueAsString(event);
System.out.println(serialisedEvent);
// {"day":"Monday","name":"Birthday"}
Event deserialisedEvent = objectMapper.convertValue(serialisedEvent, Event.class);
// Exception in thread "main" java.lang.IllegalArgumentException: Cannot construct instance of `xyz.blabla.Event` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"day":"Monday","name":"Birthday"}')
// at [Source: UNKNOWN; line: -1, column: -1]
// at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3750)
System.out.println(deserialisedEvent.getDay());
}
I am using Java 11 and Jackson 2.9 in a Spring Boot 2 project. How can I make this work?
Jackson deserialization method is called readValue.
The purpose of convertValue is different — it serializes an object (which may be a string — it would become a JSON-string-literal then) first, and then deserializes the result into an object of the target type.
The following should work:
Event deserialisedEvent = objectMapper.readValue(serialisedEvent, Event.class);
You don't have default constructor but an arg constructor.
You have to annotate it with #JsonCreator in order that Jackson uses it to deserialize the JSON :
#JsonCreator
public Event(Days day, String name) {
this.day = day;
this.name = name;
}
To serialize a Java object to JSON, the constructor is not used by Jackson since it doesn't create java instances but just use getters to retrieve properties of it. So it worked.
But to unserialize JSON to Java object, Jackson needs to instantiate the target class. By default it looks for the no arg constructor.
Note also that annotating the constructor parameters with #JsonProperty("...") is not needed if you use the ParameterNamesModule such as :
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new ParameterNamesModule());
Spring Boot 2 provides it for you as you depends on Jackson.
Here you need it because you don't use the Mapper wiring by Spring Boot but you instantiate it by yourself.
As well as the #JsonProperty annotations located in the enum are not needed either :
public enum Days {
#JsonProperty("Monday")
MONDAY("Monday"),
#JsonProperty("Tuesday")
TUESDAY("Tuesday");
//...
}
It allows to change the serialization output of the enum but actually you don't need to change it as you map it to the day field value currently used for the enum Jackson mapping...
To add to this answer, I scoured the internet looking for how to add ONE-TIME as an enum value (and have it saved with the dash). Note: enums do not allow for a dash. I fixed this by simply adding
#JsonProperty("ONE-TIME")
above the enum field declaration.
I've seen many questions around using jackson to serialize/deserialize java objects using builder patter, however, I can't figure out why this code below won't work. I'm using Jackson version 2.5.4
#JsonDeserialize(builder = User.Builder.class)
public class User {
private String name;
private User(Builder builder) {
this.name=builder.name;
}
#JsonPOJOBuilder(buildMethodName = "build")
public static class Builder {
private String name;
public Builder name(String name) {
this.name = name;
return this;
}
public User build() {
return new Learner(this);
}
}
}
Trying to output the string representation always prints an empty list {}
By default the #JsonPOJOBuilderexpects the builder methods to starts with with prefix.
You should override this in the annotation: #JsonPOJOBuilder(withPrefix = "")
You should also mark the name field with the #JsonProperty annotation, or add a getter, or use the JacksonFeatureAutoDetect feature; otherwise Jackson does not see name as a JSON property.
Following is how JSON string looks
{
"employee": {
"id": "c1654935-2602-4a0d-ad0f-ca1d514a8a5d",
"name": "smith"
...
}
}
Now i am using ObjectMapper#readValue(jsonAsStr,Employee.class) to convert it to JSON.
My Employee class is as follows...
#XmlRootElement(name="employee")
public class Employee implements Serializable {
private String id;
private String name;
...
public Employee() {
}
#XmlElement(name="id")
public String getId() {
return id;
}
public void setId(String id) {
this.id= id;
}
#XmlElement(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
...
}
The exception I am getting is
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "employee" (class com.abc.Employee), not marked as
ignorable (12 known properties: , "id", "name", ... [truncated]])
I am not able to understand why "employee" is considered as a property. Am i wrong in assuming that only class members are considered as properties?
The problem is that a JSON Object { } maps to a Java class, and the properties in the JSON map to the Java properties. The first { } in your JSON (which you are trying to unmarshal to Employee), has a property employee, which Employee class does not have a property for. That's why you are getting the error. If you were to try and unmarshal only the enclosed { }
{
"id": "c1654935-2602-4a0d-ad0f-ca1d514a8a5d",
"name": "smith"
}
it would work as Employee has those properties. If you don't have control over the JSON, then you can configure the ObjectMapper to unwrap the root value
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
But the you might have another problem. The unwrapping is based on the annotation on the Employee class, either #JsonRootName("employee") or #XmlRootElement(name = "employee"). With the latter though, you need to make sure you have JAXB annotation support. For that, you need to have the jackson-module-jaxb-annotations, then register the module
mapper.registerModule(new JaxbAnnotationModule());
This applies for all your JAXB annotations you're using. Without this module, they won't work.
#peeskillet is right.
I was looking for a long time about how to use jax annotation to deserialize the json returned from server since I was getting UnrecognizedPropertyException as well.
Adding the following code fixed my problem:
mapper.registerModule(new JaxbAnnotationModule());
Follow below the entire code i used:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JaxbAnnotationModule());
List<PojoTO>response = mapper.readValue(result.readEntity(String.class), mapper.getTypeFactory().constructCollectionType(List.class, PojoTO.class));