I have a MultivaluedMap<String, String> which represents the form parameters of a POST request. I'd like to convert a POJO from this class with only those fields I need for further processing. I found some answers which suggest using convertValue() from the Jackson ObjectMapper.
Convert a Map<String, String> to a POJO
public void process(MultivaluedMap<String, String> formParams) {
ObjectMapper objectMapper = new ObjectMapper();
final MyPojo myPojo = objectMapper.convertValue(formParams,MyPojo.class);
}
POJO
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyPojo {
private String status;
#JsonProperty("order_no")
private String orderId;
#JsonProperty("tid")
private String transactionId;
}
However this fails with the following exception:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: MyPojo["order_no"])
This is how the input looks like in the debugger:
I need to replace the String type with ArrayList<String> which seems to work:
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyPojo {
private ArrayList<String> status;
#JsonProperty("order_no")
private ArrayList<String> orderId;
#JsonProperty("tid")
private ArrayList<String> transactionId;
}
Related
I am saving below instance to Aerospike Database.
My class which I want to serialize
public class School implements Serializable {
private static final long serialVersionUID = 1L;
private JsonNode studentInfo;
private JsonNode teacherInfo;
private void writeObject(ObjectOutputStream out) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue((OutputStream) out, studentInfo);
mapper.writeValue((OutputStream) out, teacherInfo);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
ObjectMapper mapper = new ObjectMapper();
this.studentInfo = mapper.readValue((InputStream) in, JsonNode.class);
this.teacherInfo = mapper.readValue((InputStream) in, JsonNode.class);
}
}
Using above code, saving to Database is working fine(Serialization).
but when I try to get data from Database(Deserialization), I am facing below exception.
Caused by: com.aerospike.client.AerospikeException$Serialize: Error -10,1,30000,0,5,BB95B2FFB6EA79A 10.66.29.66 3030: ***com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input***
at [Source: java.io.ObjectInputStream#6ff29830; line: 1, column: 0]
at com.aerospike.client.command.Buffer.bytesToObject(Buffer.java:341)
at com.aerospike.client.command.Buffer.bytesToParticle(Buffer.java:69)
Please let me know if I am missing something.
I think we can't use ObjectOutputStream and ObjectInputStream more than one time.
I resolved it by writing writeObject and readObject function.
private void writeObject(ObjectOutputStream out) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ArrayNode arrayNode = mapper.createArrayNode().add(this.studentInfo).add(this.teacherInfo);
mapper.writeValue((OutputStream) out, arrayNode);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
ObjectMapper mapper = new ObjectMapper();
ArrayNode arrayNode = null;
arrayNode = mapper.readValue((InputStream) in, ArrayNode.class);
this.studentInfo = arrayNode.studentInfo;
this.teacherInfo = arrayNode.teacherInfo;
}
Or you can create a POJO with 2 fields as JsonNode and serialize the POJO.
I want to store my WebSession in Redis. There is no problem at put operation, but it throws exception when retrieving stored record. Here is my example stack trace
Caused by:
com.fasterxml.jackson.databind.exc.MismatchedInputException:
Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array
to contain As.WRAPPER_ARRAY type information for class
java.lang.Object at [Source:
(byte[])"{"attributes":["org.springframework.session.web.server.session.SpringSessionWebSessionStore$SpringSessionMap",{}],"id":"2a5c3d9b-3557-4bd6-bca8-9e221c3a5b41","lastAccessTime":{"nano":800305900,"epochSecond":1605268779},"creationTime":{"nano":800305900,"epochSecond":1605268779},"expired":false,"maxIdleTime":{"seconds":5400,"nano":0,"negative":false,"zero":false,"units":["java.util.ImmutableCollections$List12",[["java.time.temporal.ChronoUnit","SECONDS"],["java.time.temporal.ChronoUnit","NANOS"]]]"[truncated
18 bytes]; line: 1, column: 1]
How could I solve this problem? I don't understand why it is happening? Thanks.
Here is my session service.
#Component
#RequiredArgsConstructor
#Slf4j
public class SessionMappingStorage {
private static final String USR_TO_SESSION___KEY = "USR_SESSION_MAP";
private final ReactiveHashOperations<String, String, Object> hashOps;
public Mono<Boolean> addSession(String username, WebSession session) {
return hashOps.put(USR_TO_SESSION___KEY, username, session);
}
public Mono<WebSession> getSessionByUserId(String username) {
return hashOps.get(USR_TO_SESSION___KEY, username).cast(WebSession.class);
}
}
Here is my redis configuration.
#Bean
public ReactiveRedisTemplate<String, String> reactiveRedisTemplate() {
RedisSerializer keySerializer, hashKeySerializer, hashValueSerializer;
keySerializer = hashKeySerializer = new StringRedisSerializer();
hashValueSerializer = new GenericJackson2JsonRedisSerializer(objectMapper());
RedisSerializationContext.RedisSerializationContextBuilder<String, String> builder =
RedisSerializationContext.newSerializationContext(keySerializer);
RedisSerializationContext<String, String> context =
builder.hashKey(hashKeySerializer).hashValue(hashValueSerializer).build();
return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory(), context);
}
#Bean
public ReactiveHashOperations<String, String, Object> hashOps() {
return reactiveRedisTemplate().opsForHash();
}
private ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
return mapper;
}
I got this DTO:
#Introspected
data class SomeDTO(
val someLocalDateTime: LocalDateTime,
val someString: String
)
And I want to use it in a Micronaut Controller like this:
#Post
#Status(HttpStatus.CREATED)
fun somePostCall(
someDTO: SomeDTO,
authentication: Authentication
) {
this.someMethodCall(
someDTO.someString,
someDTO.someLocalDateTime,
authentication.name
)
}
I'm getting always this error:
Required argument [SomeDTO someDTO] not specified
I already tried to annotate the value in the DTO with #JsonFormat, #Format and with a custom TypeConverter (String to LocalDateTime) but none of them worked.
Try that ;-)
data class SomeDTO(
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
val someLocalDateTime: LocalDateTime,
val someString: String
)
If you would do it only for one class.
Otherwise you could do it also on a global level.
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
#Singleton
static class ObjectMapperBeanEventListener implements BeanCreatedEventListener<ObjectMapper> {
#Override
public ObjectMapper onCreated(BeanCreatedEvent<ObjectMapper> event) {
final ObjectMapper mapper = event.getBean();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
return mapper;
}
}
}
I am using spring data redis and one of my entity has a list as below.
#RedisHash("person")
#Data
#Builder
public class Person implements Serializable {
#Id
private String name;
private List<Address> addressList;
}
public class Address implements Serializable {
private String postCode;
private String country;
}
The serialisation works fine and the address is stored as
HGETALL person:123456
"name"
"blabla"
"address[0].postCode"
"1111XX"
"address[1].country"
"IN"
but while getting the person back the List is always null ? could someone point out what I am doing wrong here.
My Redis configuration looks as below.
#Configuration
#EnableRedisRepositories
public class RedisConfiguration {
#Bean
public JedisConnectionFactory jedisConnectionFactory() {
return new JedisConnectionFactory();
}
#Bean
public RedisTemplate<String, String> redisTemplate() {
final RedisTemplate<String, String> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
mapper.findAndRegisterModules();
template.setKeySerializer(redisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
I have some suspicion but can you share a bit more code snippet on how you use redisTemplate to save the data into redis?
But most likely its because you set up both Key and Values initialization of RedisTemplate
RedisTemplate<K, V> as <String, String>
Which is why even with Jackson2JsonSerializer it can't serialize and deserialize the List<Address> class properly.
A possible solution would be:
#Bean
public RedisTemplate<String, Person> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
RedisTemplate<String, Person> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
And then you store the whole Person object into Redis
When you need it then you get the whole object out and implement your getter and setter.
Hope this helps
I'm using fasterXML's Jackson (v2.3.3) library to deserialize and serialize a custom class. The class is defined as following:
public class Person {
private String name;
private Map<String, Person> children;
// lots of other fields of different types with no issues
}
the keys of map children are the name fields. I receive data in JSON with each person object structured as following (I have omitted the other fields):
{"name":"Bob", "children":[{"name":"Jimmmy"},{"name":"Judy"}]}
(Many Fields such as children are optional and aren't serialized when null)
I have been storing children in a List<Person> so far with no issues, but many new use cases need to have access to the set of names or to a specific Person using his name as key. This is why I have decided to store them using a Map.
After some research, I think the best way is to use Annotations #JsonDeserialize and #JsonSerialize with a JsonDeserializer and JsonSerializer as parameter respectively for the field children:
public class Person {
private String id;
#JsonSerialize(using=MySerializer.class)
#JsonDeserialize(using=MyDeserializer.class)
private Map<String, Person> friends;
// lots of other fields
}
My question is: Does such a JsonSerializer/JsonDeserializer exist and if not, how do I define one?
edit: I have started implementing a custom Deserializer, but I get this exception:
com.fasterxml.jackson.databind.JsonMappingException: Class has no default (no arg) constructor
which is weird because I have defined a default constructor. Here is my custom Deserializer:
public class MyDeserializer extends JsonDeserializer<Map<String, Person>> {
public MyDeserializer() {
}
#Override
public Map<String, Person> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode personsNodeArray = jp.getCodec().readTree(jp);
Map<String, Person> newChildren = null;
if (personsNodeArray.isArray() && personsNodeArray.size() > 0) {
newChildren = new HashMap<String, Person>();
for (JsonNode personNode : personsNodeArray) {
String id = personNode.get("name").asText();
// jsonMapper is a default ObjectMapper
newChildren.put(id, jsonMapper.readValue(personNode.toString(), Person.class));
}
}
return newChildren;
}
}
You can also consider reading children information as a collection of persons with subsequent conversion into a map. You can define a setter method (or a constructor parameter) to accept a List<Person> and then put each element into the Map<String, Person> children field. That would avoid unnecessary complexity of custom serialisation.
Here is an example:
public class JacksonChildren {
public static final String JSON = "{\"name\":\"Bob\", \"children\":[{\"name\":\"Jimmmy\"}," +
"{\"name\":\"Judy\"}]}";
public static class Person {
public String name;
private Map<String, Person> children = new HashMap<>();
public void setChildren(final List<Person> children) {
for (Person p : children) {
this.children.put(p.name, p);
}
}
#Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", children=" + children +
'}';
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(JSON, Person.class));
}
}
Output:
Person{name='Bob', children={Judy=Person{name='Judy', children={}}, Jimmmy=Person{name='Jimmmy', children={}}}}