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.
Related
I'm trying to find a solution to an issue wherein I want to serialize java objects with selected fields based on a separate list of "Fields to include". For example I have a java object class as follows:
#Data
public class ModelA implements Serializable {
private String fieldAA;
private String fieldAB;
private ModelB fieldAC;
private ModelC fieldAD;
}
#Data
public class ModelB implements Serializable {
private String fieldBA;
private String fieldBB;
private String fieldBC;
}
#Data
public class ModelC implements Serializable {
private String fieldCA;
private String fieldCB;
}
And a list of "Fields to include" - List<String> as ["fieldAA", "fieldBA", "fieldBB", "fieldCB"], I want to serizlize ModelA to only include fields from this list and ignore rest, like:
{
"fieldAA": "Value 1",
"fieldAC": {
"fieldBA": "Value 2",
"fieldBB": "Value 3"
},
"fieldAD": {
"fieldCB": "Value 4"
}
}
Thing is, the dynamic list of fields to include can change from request to request, and I only want to serialize fields which are present in this list. You can assume for now that there are finite set of requests which require a given set of response, but their number is still quite large (assume >50) to make different data transfer objects for each, when most of them logically share multiple fields as they refer similar data. (Saying this in case some kind of pool of serializers is possible)
I do not want to annotate/change modifiers of fields in model class as any field in the class can serialized for one request and not for other, so in a way each field is important for at least one request.
I understand some custom implementation of Jackson's ObjectMapper or Gson while serializing the request is the way to go (if not any other solution is also appreciated), but I'm unable to find any good resources to achieve this. Looking for something that does not impact performance very heavily (although anything will do if that is not possible).
Appreciate any help on this.
I am currently using Jackson's XmlMapper and ObjectMapper. I want to map the string to a POJO (I think I'm using that term correctly) that has a private field with the same name as the JSON string's field. The XML string has a different name for the same field/attribute, and I want to use the JSON field name.
I also want to essentially "ignore" that field (while keeping it) and store it as something like a JsonNode, as the value of that field can be some complex, nested value without a known shape.
Example:
public static class OuterClass {
private String firstValue;
private InnerClass innerValue;
// ... getters/setters
}
public static class InnerClass {
private JsonNode data; // complex, nested, so no POJO to map to
private String otherValue;
// ... getters/setters
}
The JSON might look like this:
{
"innerValue": {
"data": {
... complex stuff
},
"otherValue": "more stuff"
},
"firstValue": "thingy"
}
The XML might look like this:
<result>
<innerValue>
<incorrectName>
... complex stuff
</incorrectName>
<otherValue>more stuff</otherValue>
</innerValue>
<firstValue>thingy</firstValue>
</result>
So the goal is to get the XML to work with that class, including both mapping incorrectName to the class' data, as well as storing the complex inner part as something like a JsonNode since I don't have a class to model it.
I have the JSON working with new ObjectMapper().readValue(jsonString, OuterClass.class), and I think the XML should work with new XmlMapper().readValue(xmlString, OuterClass.class), but I don't know where to go with annotations. I've looked at the different annotations available and I don't think I've found the right one. I've also read that I shouldn't convert XML to a JsonNode, as there can be problems with that. I don't need to convert it back to XML after, though, and can treat it as JSON once I receive the JSON/XML string. So, I'd appreciate some help, thanks!
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class InnerClass {
private JsonNode data; // complex, nested, so no POJO to map to
private String otherValue;
// ... getters/setters
#XmlElement(name = "incorrectName")
protected JsonNode getData() {return data;}
}
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
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.
I have a structure of objects representing a Questionnaire and I need to serialize to JSON.
One class of the structure is a OpenQuestion and this class use generics with two parameters.
The problem starts when one of types used was Date, the date is serialized wrong, like a long.
Class code:
public class OpenQuestion <valueType,validationType> extends AbstractQuestion implements Serializable {
private valueType value;
private validationType minValue;
private validationType maxValue;
...
}
I saw how to serialize a date in a hash map if the hash map always uses a Date, but in this case I use the class with String, Integer or Date.
Any idea to solve it?
Thanks
You can add a JsonTypeInfo annotation for this. There's two ways of using this:
Get it to automatically add a type annotation to your object, so it knows what to deserialize it as.
Add a custom type resolver, to handle this for you.
The first will make your JSON ugly, but requires very little extra code and doesn't force you to make custom serializers. The latter is more difficult, but will result in cleaner JSON. Overall the problem is partly that one of your types isn't modelled in JSON (Date) so you'll probably need it to be serialised as an integer or String type in your JSON file.
The former option looks a bit like this:
#JsonTypeInfo( use = Id.CLASS, include = As.WRAPPER_PROPERTY )
private valiationType minValue;
This should encode say, a String value, as something like:
{ __type = "java.lang.String", value = "Hello, World" }
No promises on that being accurate as this is mostly from memory!
It depends. If you do know expected type, you just pass generic type reference:
OpenQuestion<Value,Validation> v = objectMapper.readValue(json,
new TypeReference<OpenQuestion<Value,Validation>>() { });
as that clues Jackson in as to expected type.
If you do not know it, then the other answer shows how to use #JsonTypeInfo.
As pointed out by #MiserableVariable, Jackson serializes (most) date fields as (numeric long) timestamps by default. You can override this behavior in a number of ways.
If using your own instance of ObjectMapper, override a property to write dates as ISO-8601:
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
If using your own instance of ObjectMapper, to have dates written in your own custom format:
objectMapper.setDateFormat(myDateFormat); // 1.8 and above
objectMapper.getSerializationConfig().setDateFormat(myDateFormat); // for earlier versions (deprecated for 1.8+)
To leave the default serialization behavior for most fields, but override it for certain fields on certain objects, use a custom serializer:
public class MyBean implements Serializable {
private Date postDate;
// ... constructors, etc
#JsonSerialize(using = MyCustomDateSerializer.class)
public Date getPostDate() {
return postDate;
}
}
public class MyCustomDateSerializer extends JsonSerializer<Date> {
#Override
public void serialize(final Date date, final JsonGeneraror generator,
final SerializerProvider provider) throws IOException,
JSONProcessingException {
generator.writeString(yourRepresentationHere);
}
}
All of this information is available in the Jackson Documentation, with the bulk of it in the section dealing with date handling.