Jackson Databind - get properties not specified in pojo - java

I'm trying to figure out how can I bind json to POJO.
When json can sometime contain additional fields depending on various conditions. Basically speaking - some part of json will always contain same properties,
for example: name and age. But sometimes I'll get shoeSize and/or eyeColor. I cant make list of all possible properties that can be passed to me, because some of them are defined by user.
Is there possibility to achieve something like this?
class MyClass
{
public String name;
public Integer age;
public Map<String, String> additionalArguments;
public MyClass(...) {...}
}

After going through documentation (again) I found annotation called JsonAnySetter and process reverting annotation JsonAnyGetter
class MyClass
{
public String name;
public Integer age;
public Map<String, String> additionalArguments = new HashMap<>();
public MyClass(...) {...}
#JsonAnyGetter
public Map<String,Object> getAdditionalProperties() {
return additionalProperties;
}
#JsonAnySetter
public void putAdditionalProperty(String name, Object value) {
additionalProperties.put(name, value);
}
}

Related

Java Spring custom Enum to String conversion in JSON Serialization

I'm trying to convert an enum value into a custom string as part of a JSON response in a Java Spring application. I've attempted to override the enum's toString method and create a Spring converter but both attempts don't seem to work.
Sample Controller
#RequestMapping(value = "/test/endpoint", produces = APPLICATION_JSON_VALUE)
#RestController
public class RecommenderController {
...
#GetMapping("test")
public List<MyEnum> test() {
return new ArrayList<>() {{
this.add(MyEnum.SAMPLE);
}};
}
}
Enum
public enum MyEnum {
SAMPLE("sample"), OTHER_SAMPLE("other sample");
private final String name;
public MyEnum(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}
This code returns the response ["SAMPLE"] although I want it to return ["sample"]. Is there a way to implement this in Spring?
Assuming you are using the default MappingJackson2HttpMessageConverter, then behind the scenes you are using Jackson's ObjectMapper to perform all the JSON serialization and deserialization. So it's a matter of configuring Jackson for your protocol objects.
In this case, it's probably most straightforward tell Jackson that it can make a single JSON value for your instance of MyEnum with the #JsonValue annotation.
public enum MyEnum {
SAMPLE("sample"), OTHER_SAMPLE("other sample");
private final String name;
public MyEnum(String name) {
this.name = name;
}
#JsonValue
public String getValue() {
return this.name;
}
}
#JsonValue has a bonus, as described in its Javadoc:
NOTE: when use for Java enums, one additional feature is that value returned by annotated method is also considered to be the value to deserialize from, not just JSON String to serialize as. This is possible since set of Enum values is constant and it is possible to define mapping, but can not be done in general for POJO types; as such, this is not used for POJO deserialization.
So if you have the same Enum definition in your application that receives the list, it will deserialize the human readable value back into your Enum.
This can be done by using the #JsonValue annotation in the enum definition:
public enum MyEnum {
...
#JsonValue
public String getName() {
return this.name;
}
}

How do I transform a dynamic json to java class?

I have a json request coming from a service. The service consist of few data entries.
{
dataPair {
keyA : valueA
keyB : valueB
....
}
name: string
addr: string
}
}
Originally I have below pojo classes
Class ServiceRequest {
public String name;
public String addr;
public DataPair dataPair;
}
Class dataPair {
public String keyA;
public String keyB;
//...
}
But now I wanted to have dataPair to be dynamic so whatever key-value pair we receive we will able to get it without changing the class.
I was wondering how should I change dataPair class or is there a way to generate key-value pair fields?
Could you use Retrofit 2.0? https://square.github.io/retrofit/
It easy works with converting dynamic json to Java collections like List or Map.
And so pojo class will be:
Class ServiceRequest {
public String name;
public String addr;
public HashMap<String, String> dataPair;
}
What about this approach:
Class dataPair {
private HashMap<String, String> dt= new HashMap<String, String>();
}

Java - How to convert a String into specific enum implementing an interface

Consider below inner Enum implementing an interface:
public interface NotificationTypes {
public enum CONTACT_LIST implements NotificationTypes{
ADDED("CONTACT_LIST-ADDED"),
REMOVED("CONTACT_LIST-REMOVED");
public enum INVITATION implements NotificationTypes{
ACCEPTED("CONTACT_LIST-INVITATION-ACCEPTED"),
REJECTED("CONTACT_LIST-INVITATION-REJECTED");
String name = "";
private INVITATION(String name){
this.name = name;
}
public String getName(){
return name;
}
};
String name = "";
private CONTACT_LIST(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public String getName();
}
Now consider that data in database/mongodb is stored in the form of String for NotificationTypes in a table/document.
{
"_id" : ObjectId("59882ba49e5d82c72ba44fde"),
"template" : "Contact list Invitation accepted by your friend",
"type" : "CONTACT_LIST-INVITATION-ACCEPTED"
}
So my question is: How to convert that string back into specific enum at runtime without knowing exactly the name of enum to be mapped?
Domain class is looks like this:
#Document(collection = CollectionNames.XXX_TEMPLATE)
public class XXXTemplate {
private NotificationTypes type;
//Other parameters, getters & setters + Constructor
}
I'd build a Map<String, NotificationTypes> and populate that with all instances you have. You can then look up from that map.
I don't think the compiler can help you a lot with keeping that in sync, other than that you can loop over EnumType.values() (but you have to remember to do it for all of your enum types).
How to convert that string back into specific enum at runtime without knowing exactly the name of enum to be mapped?
Via Enum.valueOf().
Basically just building on #Thilo's answer, but perhaps a more 'Springified' way if it's something that you'd want - you could define a #Bean in your config that contains all your enum values, like:
#Configuration
public class Config {
#Bean
public List<NotificationTypes> notificationTypes() {
List<NotificationTypes> notificationTypes = new ArrayList<>();
notificationTypes.addAll(Arrays.asList(NotificationTypes.CONTACT_LIST.values()));
notificationTypes.addAll(Arrays.asList(NotificationTypes.CONTACT_LIST.INVITATION.values()));
return notificationTypes;
}
}
And then #Autowire this #Bean into a parser to do the actual matching of String to enum, something like:
#Component
public class NotificationTypeParser {
#Autowired
private List<NotificationTypes> notificationTypes;
public NotificationTypes parseNotificationType(String type) {
for (NotificationTypes notificationType : notificationTypes) {
if (notificationType.getName().equals(type)) {
return notificationType;
}
}
return null;
}
}
Obviously you probably want something better than just returning null if the enum isn't found, and you could potentially do something smarter in the #Bean definition to validate that the enums all have different names, etc. Or, conceivably, use reflection in there to find all the implementations of NotificationTypes.
I'm not sure that this really gives you any additional benefits over just storing all the possible values in a Map, but, as I say, I suppose it's a bit Spring-ier.

JACKSON with JSON: map unrecognized fields [duplicate]

This question already has answers here:
Collecting unknown properties with Jackson
(2 answers)
Closed 6 years ago.
I need to convert a JSON string into a Java object. The JSON will have a few known fields and some unknown ones. Here is an example:
public class MyJsonBean {
private String abc;
private String def;
// getters and setters
}
And the JSON I want to parse:
{"abc":"value1","def":"value2","ghi":"value3","jkl":"value4"}
Only fixed fields are "abc" and "def". Other fields are variable.
I'd like Jackson to parse the variable fields and put them into a list/map within the MyJsonBean class. Is there any way to do that?
Use the #JsonAnySetter Called by json deserialization to store non-member elements of the json object. Stores the value in the otherAnnotations field.
Jackson can actually be made to work with such POJOs: here is one way to do it:
public class MyJsonBean
{
// Two mandatory properties
protected final String abc;
protected final String def;
// and then "other" stuff:
protected Map<String,Object> other = new HashMap<String,Object>();
// Could alternatively add setters, but since these are mandatory
#JsonCreator
public MyJsonBean (#JsonProperty("abc") String abc, #JsonProperty("def") String def)
{
this.abc = abc;
this.def = def;
}
public int getId() { return id; }
public String getName() { return name; }
public Object get(String name) {
return other.get(name);
}
// "any getter" needed for serialization
#JsonAnyGetter
public Map<String,Object> any() {
return other;
}
#JsonAnySetter
public void set(String name, Object value) {
other.put(name, value);
}
}
And there we have it: serializes and deserializes nicely.
Share and enjoy... :)

Serialize/deserialize immutable objects that carry extra informations with Jackson

I try to use only immutables objects in my application. I've got a REST service that will take arbitrary JSon objects as input.
I've a Java class that map theses objects, and I want to make them immutable + able to deal with extra parameters (just like using #JsonAnySetter).
Here is my java class:
public class Operation {
private final String _id;
private final String state;
private final Map<String, Object> extra;
public Operation(String _id, String state, Map<String,Object> extra) {
this._id = _id;
this.state = state;
this.extra = extra;
}
// getters....
}
Using #JsonAnySetter I would have:
public class Operation {
private final String _id;
private final String state;
private Map<String, Object> extra = new HashMap<>();
public Operation(String _id, String state) {
this._id = _id;
this.state = state;
}
#JsonAnySetter
public void addExtra(String key, Object value) {
this.extra.put(key,value);
}
// getters....
}
But this is not immutable anymore !
This will not work because Jackson do not find any "extra" json attribute to read. I would like that everything that cannot be mapped be added to my map.
Any idea of how to do this ? (or is it just possible :)
Note: I use javac with -parameters option and the ParameterNameModule from jackson so that I don't need #JsonCreator option.
Ok so I respond to myself :)
It seems that it is not possible to do that using only Jackson.
Because I want immutability, I've turned myself to the 'immutables' framework: http://immutables.github.io/
With a little configuration, it will deal with extra parameters as stated in the following report: https://github.com/immutables/immutables/issues/185.
In my situation, I've got the following code:
#Value.Immutable
#JsonSerialize(as = ImmutableOperation.class)
#JsonDeserialize(as = ImmutableOperation.class)
public abstract class Operation {
#JsonAnyGetter
#Value.Parameter
public abstract Map<String, String> extra();
}
Refer to the documentation of immutables for the details.
If you want to deserialize immutable entity with extra arguments you can utilize builder pattern:
#JsonPOJOBuilder
public class OperationBuilder {
private String _id;
private String _state;
private Map<String, Object> extra = new HashMap<>();
#JsonAnySetter
public OperationBuilder addExtra(String key, Object value) {
this.extra.put(key,value);
return this;
}
// setters....
public Operation build() {
return new Operation(...arguments...)
}
And your original class should have this annotation on a class level:
#JsonDeserializer(builder = OperationBuilder.class)
This way all your known and unknown (extra) fields will be populated inside the builder and then Jackson will call build() method at the end of the deserialization.

Categories