Unrecognised Property with Jackson #JsonCreator - java

I'm trying to use Jackson to convert some JSON into an instance of a class that contains some simple Strings and another class, which I'm using #JsonCreator for. It seems that Jackson isn't able to create the instance of the other class.
The problem is that when I run this code as part of a test:
ObjectMapper mapper = new ObjectMapper();
Player player = mapper.readValue(json.toString(), Player.class);
I get the following exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "characterClass" (class xxx.xxx.Player), not marked as ignorable (2 known properties: "name", "character"])
The JSON I'm trying to parse in my simple test looks like this:
{
"name": "joe",
"characterClass": "warrior",
"difficulty": "easy",
"timesDied": 2
}
I have a class 'Player' that looks a bit like this
public class Player {
#JsonProperty("name")
private String playerName;
#JsonProperty // <-- This is probably wrong
private Character character;
// Some getters and setters for those two fields and more
}
And another class 'Character' that looks like this
public class Character{
private PlayerClass playerClass;
private Difficulty difficulty;
private int timesDied;
#JsonCreator
public Character(#JsonProperty("characterClass") String playerClass,
#JsonProperty("difficulty") String diff,
#JsonProperty("timesDied") int died) {
// Validation and conversion to enums
this.playerClass = PlayerClass.WARRIOR;
this.difficulty = Difficulty.EASY;
this.timesDied = died;
}
// Again, lots of getters, setters, and other stuff
}
For small sets of data like this there would be better ways to structure the whole thing, but I think this works for the purposes of an example. The real code I have is more complex but I wanted to make simple example.
I think I've messed up the Jackson annotations, but I'm not sure what I've done wrong.

You need to specify a creator on Player that matches your JSON input. For example:
#JsonCreator
public static Player fromStringValues(#JsonProperty("name") String name,
#JsonProperty("characterClass") String characterClass,
#JsonProperty("difficulty") String difficulty,
#JsonProperty("timesDied") Integer timesDied) {
Player player = new Player();
player.setPlayerName(name);
player.setCharacter(new Character(characterClass, difficulty, timesDied));
return player;
}
A side note, you can structure your enums like this and Jackson will do the conversion from string to enum for you.

Related

Diff two classes fields names and structure

My task
Force two classes to have the same (or similar) field names (and their types)
Description
I have an entity and Data Transfer Objects (DTO).
How do I force that if someone adds / removes / changes a field in entity, a test will fail, so DTO class matches the entity class by fields names and if possible by fields structure.
class City {
private String name;
private CityDetails cityDetails;
private Mayor mayor;
}
class Mayor {
private String name;
private LocalDate electionFrom;
private LocalDate electionTo;
}
class CityDto {
private String name;
private CityDetailsDto cityDetails;
private MayorDto mayor;
}
class MayorDto {
private String name;
// The client of the end-point of such DTO does not care about the mayor election.
}
CityDetails and CityDetailsDto can be different or not necessary. I would like to have their diff.
Approaches
Using so called diff tool
Let's imagine a diff tool which has input parameters - two objects and output parameter - Map, where Diff is a structure oldValue, newValue. This tool returns difference between input arguments.
public Map<String, Diff> diff(final Object first, final Object second) {
// This is implemented.
return innerDiff(first, second, otherParameters); //
}
public class Diff {
private String oldValue;
private String newValue;
// getters, setters, constructor.
}
// To achieve this, we used Guava Plain map. It works well!
How do I achieve the same for classes. I want to diff two classes and have their fields as difference.
public Map<String, FieldDiff> diff(Class<?> type1, Class<?> type2) {
// How?
}
One idea is to use reflection and iterate though all fields of the class.
How about using toString and compare?
As an alternative, we define toString() methods in both and then compare.
How to do that if some fields are missing?
Serialize into JSON
Similar to the previous one, but serializing both objects into JSON and then compare their classes by json field names. However, if our entity have already annotation DoNotSerializeNulls with is equal to #JsonInclude(JsonInclude.Include.NON_NULL) #JsonIgnoreProperties(ignoreUnknown = true), then how to do that?
References
https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application

Wrapping Json fields into instance variable of a pojo

i am trying to map certain json fields to a class instance variable.
My sample Person class looks like:
public class Person {
private String name;
private Address address;
//many more fields
//getters and setters
}
The sample Address class is:
public class Address {
private String street;
private String city;
//many more fields
// getters and setters
}
The json object to be deserialized to my Person class doesn't contain "address" field. It looks like:
{
"name":"Alexander",
"street":"abc 12",
"city":"London"
}
Is there a way to deserialize the json to the Person pojo where the Address fields are also mapped properly?
I have used a custom Address deserializer as mentioned in so many posts here. However, it's not being called as the Json object doesn't contain "address" field.
I had resolved this problem by mapping each field manually using JsonNode, however in my real project, it's not a nice solution.
Is there any work around for such problem using jackson?
Plus if this question has been asked before then apologies on my behalf as as i have intensively searched for the solution and might have not seen it yet. .
#JsonUnwrapped annotation was introduced for this problem. Model:
class Person {
private String name;
#JsonUnwrapped
private Address address;
// getters, setters, toString
}
class Address {
private String street;
private String city;
// getters, setters, toString
}
Usage:
ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"Alexander\",\"street\":\"abc 12\",\"city\":\"London\"}";
System.out.println(mapper.readValue(json, Person.class));
Prints:
Person{name='Alexander', address=Address{street='abc 12', city='London'}}
For more info read:
Jackson Annotation Examples
Annotation Type JsonUnwrapped
Jackson JSON - Using #JsonUnwrapped to serialize/deserialize properties as flattening data structure
I don't think you really have a deserialization problem here but rather a general Java problem: how to make sure the address field always contains a value. All you need to do is either assign address to a default value in the Person constructor, or generate and assign a default value for address in the Person.getAddress method.
I understood your problem so that it is about flat Json that has all Address fields at the same level as Person. Even if it is not exactly so this might help you. JsonDeserializer will do fine but you need to apply it to Person because it is the level where all the fields are.
So like this:
public class CustomDeserializer extends JsonDeserializer<Person> {
// need to use separate ObjectMapper to prevent recursion
// this om will not be registered with this custom deserializer
private final ObjectMapper om;
{
om = new ObjectMapper();
// this is needed because flat json contains unknown fields
// for both types.
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
#Override
public Person deserialize(JsonParser parser, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// make a string of json tree so not any particular object
String json = om.readTree(parser).toString();
// deserialize it as person (ignoring unknown fields)
Person person = om.readValue(json, Person.class);
// set address deserializing it from teh same string, same manner
person.setAddress(om.readValue(json, Address.class));
return person;
}
}
Of course this is not the only way and might not have the best performance but it is only about how you do the deserialization in your custom deserializer. If your Person & Address objects are havin like 10 fields each using this should not be a problem.
Update
I think that in your case - based on your example data - MichaƂ Ziober's
answer might be the best but if you need any more complex handling than plain unwrapping for your data you just need to deserialize Person class somehow like I presented.

Convert complex JSON to one-level POJO using Jackson

Let's imagine you have the following JSON:
{
"prop0": "value0",
"level1" : {
"prop1": "value1"
"prop2": "value2"
},
....
}
Can it be turned to simple Java object?
class Pojo {
private String prop0;
private String prop1;
private String prop2;
}
I don't want to create a intermediate class to wrap "level1".
What is come to my mind is to map my class in this way:
class Pojo {
private String prop0;
#JsonProperty("level1.prop1")
private String prop1;
#JsonProperty("level1.prop2")
private String prop2;
}
But unfortunately it doesn't work. The inverse problem - turn complex Java object to plain JSON can be simply solved using #JsonUnwrapped annotation.
Can you please suggest any workable solution for my issue?
You need to either write a custom deserializer, or add a setter that can transform the structure. For latter you could do something like
...
public void setLevel1(Map<String,String> values) { // or JsonNode
prop1 = values.get("prop1");
// and so forth; if names are regular, can use looping
}

Using Reserved names with realm.io / Android

Imagine there's an API that returns something like this:
"names":{
"short":"xxx",
"medium":null,
"long":"xxxxxx"
},
(just an example)
Then imagine that you have a model to represent the above that looks like this:
public class Names extends RealmObject {
private String short;
private String medium;
private String long;
// getters/setters omitted for clarity
}
There's a problem, since both short and long are reserved keywords in Java.
Other ORMs present an annotation (usually #Key("othername")) to deal with these scenarios.
What would be realm.io's solution?
Thanks!
Christian from Realm here. Our JSON support is still pretty simple, but we plan to address issues like those in the next iteration. Currently you have two options: Either convert the JSON to JSONObject and manually map the fields in a static methods like this:
public class Names extends RealmObject {
...
public static Names fromJson(JSONObject json) {
Names names = new Names();
names.setShortField(json.getString("short");
return names;
}
}
or alternatively if you don't mind including other 3rd party libraries, you can use GSON which has a #SerializedName annotation.

Jackson Json Type Mapping Inner Class [duplicate]

This question already has answers here:
How to serialize complex Json object to QueryString for HTTP Get using Jackson?
(3 answers)
Converting java object with nested classes to Json
(1 answer)
Custom Jackson Mapping for Inner Classes
(2 answers)
Closed 7 years ago.
I am trying to create the inner class type for an object being passed in as JSON but while I have read tons on here as well as Jackson's site I don't seem to be able to get the right combination so if anyone else has any pointers they would be much appreciated. I've posted some snippets below and removed all getters and setters, I didn't figure they needed posting. I'm using Jackson 2.2.
The classes I'm attempting to deserialize:
public class Settings {
private int offset;
private int limit;
private String type;
private Map<String, Criteria> criteria;
public class Criteria {
private String restriction;
private Object value;
}
}
The code I'm using to deserialize:
ObjectMapper om = new ObjectMapper();
TypeFactory tf = om.getTypeFactory();
JavaType map = tf.constructMapLikeType( Map.class, String.class, Criteria.class );
JavaType type = typeFactory.constructType( Settings.class, map );
Settings settings = om.readValue( entity, type );
My JSON testing data:
{ "type": "org.json.Car", "criteria": { "restriction": "eq", "value": "bmw" } }
The correct answer is that you are missing the static keyword on the inner class.
Just make sure that the "static" keyword is there.
Read http://www.cowtowncoder.com/blog/archives/2010/08/entry_411.html
it takes you 3 minutes but make you happy for the rest of the day.
If you can, then make your life simple and move the inner class to a normal class with a reference in the Settings class. And then do the marshalling using jackson, here is how you can have your classes:
public class Settings {
private int offset;
private int limit;
private String type;
private Map<String, Criteria> criteria;
private Criteria criteria;
}
class Criteria {
private String restriction;
private Object value;
}

Categories