Java serializer/deserializer for List<Long> - java

I need to make serializer and deserializer, because I get this error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.Long` out of START_ARRAY token
And I make it:
public class CalculatorIDsDeserializer extends JsonDeserializer<Long> {
#Override
public Long deserialize(final JsonParser jsonParser,
final DeserializationContext deserializationContext)
throws IOException {
String dataString = jsonParser.getText();
return Long.parseLong(dataString);
}
}
and
public class CalculatorIDsSerializer extends JsonSerializer<Long>{
#Override
public void serialize(Long aLong, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(Long.toString(aLong));
}
}
but now I have this error:
com.fasterxml.jackson.databind.JsonMappingException: org.hibernate.collection.internal.PersistentBag cannot be cast to java.lang.Long
And I make it for:
#JsonDeserialize(using = CalculatorIDsDeserializer.class)
#JsonSerialize(using = CalculatorIDsSerializer.class)
private List<Long> insuranceFileIDs = new ArrayList<>();
Some tips how to solve it? I have no more ideas after a few hours. Thaks
Update: I change it to this, but I get same error :(
public class CalculatorIDsDeserializer extends JsonDeserializer<List<Long>> {
#Override
public List<Long> deserialize(final JsonParser jsonParser,
final DeserializationContext deserializationContext)
throws IOException {
String dataString = jsonParser.getText();
List<Long> ids = null;
ids.add(Long.parseLong(dataString));
return ids;
}
}
Update2:
public class CalculatorIDsSerializer extends JsonSerializer<List<Long>>{
#Override
public void serialize(List<Long> longs, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
longs.forEach(item ->{
try {
jsonGenerator.writeString(Long.toString(item));
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
and
public class CalculatorIDsDeserializer extends JsonDeserializer<List<Long>> {
#Override
public List<Long> deserialize(final JsonParser jsonParser,
final DeserializationContext deserializationContext)
throws IOException {
String dataString = jsonParser.getText();
List<Long> ids = new ArrayList<>();
ids.add(Long.parseLong(dataString));
return ids;
}
}
And i get: Problem deserializing property 'insuranceFileIDs' (expected type: [simple type, class java.lang.Long]; actual type: java.util.ArrayList), problem: argument type mismatch

Related

Java Jackson: why do I get JsonMappingException although I used #JsonDeserialize

I need some help with Jackson library in Java. I have the following class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MarketHistoryData {
private String countryCode;
#JsonDeserialize(keyUsing = BpTimeDeserializer.class)
private Map<BpTime, Double> hourToExpectedEngagePaidListings;
#JsonDeserialize(keyUsing = BpTimeDeserializer.class)
private Map<BpTime, Double> hourMinuteToExpectedEngagePaidListings;
public MarketHistoryData() {}
...
// getters and setters
}
I understood that Jackson has difficulties with deserializing a map which its keys are an object. Therefore, I added the annotations:
#JsonDeserialize(keyUsing = BpTimeDeserializer.class)
The class BpTimeDeserializer is:
public class BpTimeDeserializer extends KeyDeserializer {
private ObjectMapper mapper = new ObjectMapper();
#Override
public BpTime deserializeKey(String key, DeserializationContext ctxt) throws IOException {
return mapper.readValue(key, BpTime.class);
}
}
Still, I get an error during the deserialization process:
*****.UncheckedExecutionException:
com.fasterxml.jackson.databind.JsonMappingException: Unrecognized
token 'com': was expecting (JSON String, Number, Array, Object or
token 'null', 'true' or 'false') at [Source:
(String)"****BpTime#5922062e[hour=1,minute=0]";
line: 1, column: 1] (through reference chain:
***PacerCycleOutput["campaignIdToFactorComputationData"]->java.util.LinkedHashMap["1011620661"]->****FactorComputationData["selectedMarketHistoryDataForCampaign"]->***MarketHistoryData["hourToExpectedEngagePaidListings"])
Do you know what I can do to overcome this error?
I finally found a way how to resolve this error.
#JsonIgnoreProperties(ignoreUnknown = true)
public class MarketHistoryData {
private String countryCode;
#JsonDeserialize(keyUsing = BpTimeDeserializer.class)
#JsonSerialize(keyUsing = BpTimeSerializer.class)
private Map<BpTime, Double> hourToExpectedEngagePaidListings;
#JsonDeserialize(keyUsing = BpTimeDeserializer.class)
#JsonSerialize(keyUsing = BpTimeSerializer.class)
private Map<BpTime, Double> hourMinuteToExpectedEngagePaidListings;
...
}
public class BpTimeDeserializer extends KeyDeserializer {
private ObjectMapper mapper = new ObjectMapper();
#Override
public BpTime deserializeKey(String key, DeserializationContext ctxt) throws IOException {
String[] keySplit = key.split(" ");
BpTime bpTime = new BpTime();
if (!keySplit[0].equals("-1")) {
bpTime.setHour(Integer.parseInt(keySplit[0]));
}
if (!keySplit[1].equals("-1")) {
bpTime.setMinute(Integer.parseInt(keySplit[1]));
}
return bpTime;
}
}
public class BpTimeSerializer extends StdSerializer<BpTime> {
public BpTimeSerializer() {
this(null);
}
public BpTimeSerializer(Class<BpTime> t) {
super(t);
}
#Override
public void serialize(BpTime value, JsonGenerator generator, SerializerProvider arg2) throws IOException {
generator.writeFieldName(String.format("%s %s", ofNullable(value.getHour()).orElse(-1),
ofNullable(value.getMinute()).orElse(-1)));
}
}

How to prevent PatchUpdateException on json deserialization?

I'm parsing json with custom desereailizer. It reads JsonNode and then deserializes entity from json by ObjectMapper.
public static class CustomDeserializer extends StdDeserializer<Custom> implements ResolvableDeserializer {
private final JsonDeserializer<?> defaultDeserializer;
public CustomDeserializer(JsonDeserializer<?> defaultDeserializer) {
super(Custom.class);
this.defaultDeserializer = defaultDeserializer;
}
#Override
public Custom deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
final ObjectNode node = jp.readValueAsTree();
// some node structure validation
try (final TreeTraversingParser newJsonParser = new TreeTraversingParser(node)) {
newJsonParser.nextToken();
return (Custom) defaultDeserializer.deserialize(newJsonParser, ctxt);
}
}
#Override
public void resolve(DeserializationContext ctxt) throws JsonMappingException {
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
}
}
On some requests it works, on other throws
com.fasterxml.jackson.core.json.UTF8StreamJsonParser cannot be cast to com.fasterxml.jackson.databind.node.TreeTraversingParser
or
m.fasterxml.jackson.core.json.ReaderBasedJsonParser cannot be cast to com.fasterxml.jackson.databind.node.TreeTraversingParser
How to prevent that case?

Generic Enum Json Deserialization

I needed a better hibernate enum mapping and this page served me well (Except I used char type instead of int).
Next question is how can I serialize/deserialize an enum in a generic way?
Think of a Gender enum:
#JsonSerialize(using = PersistentEnumSerializer.class)
#JsonDeserialize(using = PersistentEnumDeserializer.class)
public enum Gender implements PersistentEnum {
MALE("M", "Male"), FEMALE("F", "Female");
private String code;
private String display;
Gender(String code, String display) {
this.code = code;
this.display = display;
}
public String getName() {
return name();
}
public String getCode() {
return code;
}
public String getDisplay() {
return display;
}
public String toString() {
return display;
}
}
which implements getName(), getCode() and getDisplay() methods of PersistentEnum interface. Serializing is easy:
public class PersistentEnumSerializer extends JsonSerializer<PersistentEnum> {
#Override
public void serialize(PersistentEnum object, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("name");
generator.writeString(object.getName());
generator.writeFieldName("code");
generator.writeString(object.getCode());
generator.writeFieldName("display");
generator.writeString(object.getDisplay());
generator.writeEndObject();
}
}
but how can I deserialize in java 6? In java 8, I would add a static method to PersistentEnum interface.
public class PersistentEnumDeserializer extends JsonDeserializer<PersistentEnum> {
#Override
public PersistentEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
//String value = node.get("name").asText();
//TODO Somehow I need to get Gender.MALE if the json is {"name":"MALE","code":"M","display":"Male"}
return null;
}
}
One possible solution is to add a new method getType() to PersistentEnum which will identify the type of Enum.
#JsonSerialize(using = PersistentEnumSerializer.class)
#JsonDeserialize(using = PersistentEnumDeserializer.class)
public enum Gender implements PersistentEnum {
#Override
public String getType() {
return "gender";
}
}
Serializer should also be modified to include type while serialization.
public class PersistentEnumSerializer extends JsonSerializer<PersistentEnum> {
#Override
public void serialize(PersistentEnum object, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("name");
generator.writeString(object.getName());
generator.writeFieldName("code");
generator.writeString(object.getCode());
generator.writeFieldName("display");
generator.writeString(object.getDisplay());
generator.writeFieldName("type");
generator.writeString(object.getType());
generator.writeEndObject();
}
}
Deserializer can be written as shown below.
public class PersistentEnumDeserializer extends JsonDeserializer<PersistentEnum> {
#Override
public PersistentEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
return findEnum(node.get("type").asText(), node.get("name").asText());
}
private PersistentEnum findEnum(String type, String name) {
switch (type) {
case "gender":
return Gender.valueOf(name);
// handle other types here.
default:
return null;
}
}
}
While #Justin Jose's solution is not the one I'm looking for (because for each enum we need to add to findEnum method), it gave me a good hint.
If getType is implemented like this:
#Override
public String getType() {
return getClass().getSimpleName();
}
and findEnum like this
private PersistentEnum findEnum(String type, String name) {
Class<?> c = null;
try {
c = Class.forName("enums." + type); //Assuming all PersistentEnum's are in "enums" package
if (PersistentEnum.class.isAssignableFrom(c)) {
Method method = c.getMethod("name");
for (Object object : c.getEnumConstants()) {
Object enumName = method.invoke(object);
if (name.equals(enumName))
return (PersistentEnum) object;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
it may work. Not tested and possibly vulnerable.

JSON - show output as <key> : <value> pair [duplicate]

I have a class
class Foo {
String key;
String value;
}
and want to serialize this into "<content of key>":"<content of value>"
How can I achieve this (and how to deserialize "myKey":"myVal" into a Fooobject?
I was trying to use
#JsonValue
public String toString() {
return "\"" + key + "\":\"" + value + "\"";
}
But clearly end up with too many quotes.
#JsonValue
public String toString() {
return key + ":" + value;
}
also does not work, as it does not create enough quotes.
I found one way, which is using a JsonSerializer like this:
public class PropertyValueSerializer extends JsonSerializer<Foo> {
#Override
public void serialize(Foo property_value, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeStartObject();
jsonGenerator.writeFieldName(property_value.getKey());
jsonGenerator.writeString(property_value.getValue());
jsonGenerator.writeEndObject();
}
The Foo class needs to know about this:
#JsonSerialize(using = PropertyValueSerializer.class)
public class Foo {
Deserializing is very similar:
public class PropertyValueDeserializer extends JsonDeserializer<PROPERTY_VALUE> {
#Override
public Foo deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String tmp = jsonParser.getText(); // {
jsonParser.nextToken();
String key = jsonParser.getText();
jsonParser.nextToken();
String value = jsonParser.getText();
jsonParser.nextToken();
tmp = jsonParser.getText(); // }
Foo pv = new Foo(key,value);
return pv;
}
And this also needs to be annotated on the Foo class:
#JsonSerialize(using = PropertyValueSerializer.class)
#JsonDeserialize(using = PropertyValueDeserializer.class)
public class Foo implements Serializable{

Serialize/Deserialize custom Map<Key, Object> in Jackson

I have a pretty simple Map I want to serialize and deserialize in Jackson, but I can't get it to work.
I have tried the following:
#JsonSerialize(keyUsing=TurnKeySerializer.class)
#JsonDeserialize(keyUsing = TurnKeyDeserializer.class)
Map<TurnKey, PlayerTurn> publicTurns = new TreeMap<>();
#JsonIgnoreProperties(ignoreUnknown = true)
#Data //Creates Getter/Setter etc
public class TurnKey implements Comparable<TurnKey> {
private final int turnNumber;
private final String username;
public TurnKey(int turnNumber, String username) {
this.turnNumber = turnNumber;
this.username = username;
}
#Override
public int compareTo(TurnKey o) {
int v = Integer.valueOf(turnNumber).compareTo(o.getTurnNumber());
if (v != 0) {
return v;
}
return username.compareTo(o.getUsername());
}
#Override
public String toString() {
return "{" +
"turnNumber:" + turnNumber +
", username:'" + username + "'" +
"}";
}
public class TurnKeySerializer extends JsonSerializer<TurnKey> {
#Override
public void serialize(TurnKey value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
if (null == value) {
throw new IOException("Could not serialize object to json, input object to serialize is null");
}
StringWriter writer = new StringWriter();
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(writer, value);
gen.writeFieldName(writer.toString());
}
}
public class TurnKeyDeserializer extends KeyDeserializer {
private static final ObjectMapper mapper = new ObjectMapper();
#Override
public TurnKey deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return mapper.readValue(key, TurnKey.class);
}
}
But I get an exception
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.LinkedHashMap out of START_ARRAY token
You need to define/override the fromString() method in TurnKey. Jackson will use toString() to serialize and fromString() to deserialize. That's what "Can not find a (Map) Key deserializer" means in the error message Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not find a (Map) Key deserializer for type [simple type, class no.asgari.civilization.server.model.TurnKey] at com.fasterxml.jackson.databind.deser.DeserializerCache._handleUnknownKeyDeserializer(DeserializerCache.java:584).
A custom KeyDeserializer is not needed.

Categories