I have this situation and I'm not sure why is this happening, maybe you can help me on this.
I have a class MyClass with this structure. (with getters & setters)
public class MyClass {
private String id;
private String title;
private boolean anyBoolean;
And I added some static attributes such us
public class MyClass {
public static final Group CONS_1 = new Group("cons_1","Cons 1", false);
public static final Group CONS_2 = new Group("cons_2","Cons 2", true);
private String id;
private String title;
private boolean anyBoolean;
public MyClass(String id, String title, boolean anyBoolean) {
this.id = id;
this.title = title;
this.anyBoolean = anyBoolean;
}
Just FYI, I didn't use enums because I have to serialize all the attributes and with the enums it was serializing only the name.
Later I realize it will good to have a way to expose these constants by ID such as
public static Map<String, MyClass> myMap;
I tried a basic approach like
static {
myMap = new HashMap<>();
myMap.put(CONS_1.getId(), CONS_1);
}
and it worked, obviously.
But I wondered if I could do this with Stream, like
myMap = Stream.of(CONS_1, CONS_2).collect(Collectors.groupingBy(MyClass::getId));
But is not working, because getId() is non static.
So my question is why the second way is not working since both approaches looks equivalent?
Thanks!
Finally I make it work. The proper way to create that map is
myMap = Stream.of(CONS_1, CONS_2).collect(Collectors.toMap(MyClass::getId, Function.identity()));
I don't understand why the compiler complained about the static method though.
Related
I am trying to see if I can replace my existing Pojos with the new Record classes in Java 14. But unable to do so. Getting following error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot
construct instance of com.a.a.Post (no Creators, like default
construct, exist): cannot deserialize from Object value (no delegate-
or property-based Creator)
I get that the error is saying the record has no constructors, but from what I see the record class takes care of it in the background and relevant getters are also set in the background (not getters exactly but id() title() and so on without the get prefix). Is it cos Spring has not adopted the latest Java 14 record yet? Please advice. Thanks.
I am doing this in Spring Boot version 2.2.6 and using Java 14.
The following works using the usual POJOs.
PostClass
public class PostClass {
private int userId;
private int id;
private String title;
private String body;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
Method to call rest service which works now as I am using the above POJO.
public PostClass[] getPosts() throws URISyntaxException {
String url = "https://jsonplaceholder.typicode.com/posts";
return template.getForEntity(new URI(url), PostClass[].class).getBody();
}
But if I switch to following where I am using record instead, I am getting the above error.
The new record class.
public record Post(int userId, int id, String title, String body) {
}
Changing the method to use the record instead which fails.
public Post[] getPosts() throws URISyntaxException {
String url = "https://jsonplaceholder.typicode.com/posts";
return template.getForEntity(new URI(url), Post[].class).getBody();
}
EDIT:
Tried adding constructors as follows to the record Post and same error:
public record Post(int userId, int id, String title, String body) {
public Post {
}
}
or
public record Post(int userId, int id, String title, String body) {
public Post(int userId, int id, String title, String body) {
this.userId = userId;
this.id = id;
this.title = title;
this.body = body;
}
}
It is possible with some Jackson Annotations, which cause Jackson to use fields instead of getters. Still far less verbose than a pre-Java 14 class (without Lombok or similar solutions).
record Foo(#JsonProperty("a") int a, #JsonProperty("b") int b){
}
This probably works because according to https://openjdk.java.net/jeps/359:
Declaration annotations are permitted on record components if they are
applicable to record components, parameters, fields, or methods.
Declaration annotations that are applicable to any of these targets
are propagated to implicit declarations of any mandated members.
See also: When is the #JsonProperty property used and what is it used for?
It is also possible to make use #JsonAutoDetect
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
record Bar(int a, int b){
}
If configuring the Objectmapper to use field Visibility globally, this annotation on class level is not needed.
See also: How to specify jackson to only use fields - preferably globally
Example:
public class Test {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper om = new ObjectMapper();
System.out.println(om.writeValueAsString(new Foo(1, 2))); //{"a":1,"b":2}
System.out.println(om.writeValueAsString(new Bar(3, 4))); //{"a":3,"b":4}
}
record Foo(#JsonProperty("a") int a, #JsonProperty("b") int b){
}
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
record Bar(int a, int b){
}
}
There is also a Github issue for that feature: https://github.com/FasterXML/jackson-future-ideas/issues/46
This is slated for jackson 2.12
https://github.com/FasterXML/jackson-future-ideas/issues/46
The compiler generates the constructor and other accessor method for a Record.
In your case,
public final class Post extends java.lang.Record {
public Post(int, int java.lang.String, java.lang.String);
public java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public int userId();
public int id();
public java.lang.String title();
public java.lang.String body();
}
Here you can see that there is not default constructor which is needed got Jackson. The constructor you used is a compact constructor,
public Post {
}
You can define a default/no args constructor as,
public record Post(int userId, int id, String title, String body) {
public Post() {
this(0,0, null, null);
}
}
But Jackson uses Getter and Setters to set values. So in short, you can not use Record for mapping the response.
EDIT as PSA: Jackson can properly serialize and deserialize records as of 2.12 which has been released.
Use the parameter names module for jackson, https://github.com/FasterXML/jackson-modules-java8/tree/master/parameter-names (make sure the compiler sets -parameters) or add `#JsonProperty("name") to each field in the record
add #JsonCreator to the constructor. I can't tell if the inheritance will work properly, so you might have to explicitly declare the constructor and annotate it.
If a public accessor method or (non-compact) canonical constructor is declared explicitly, then it only has the annotations which appear on it directly; nothing is propagated from the corresponding record component to these members.
From https://openjdk.java.net/jeps/384
So add
new ObjectMapper().registerModules(new ParameterNamesModule())
and try
#JsonCreator record Value(String x);
or something like
record Value(String x) {
#JsonCreator
public Value(String x) {
this.x = x;
}
}
or all the way to
record Value(#JsonProperty("x") String x) {
#JsonCreator
public Value(#JsonProperty("x") String x) {
this.x = x;
}
}
This is how I get immutable pojos with lombok and jackson to work, and I don't see why records wouldn't work under the same format. My setup is Jackson parameter names module, -parameters compiler flag for java 8 (I don't think this is required for like jdk9+), #JsonCreator on the constructor. Example of a real class working with this setup.
#Value
#AllArgsConstructor(onConstructor_ = #JsonCreator)
public final class Address {
private final String line1;
private final String line2;
private final String city;
private final String region;
private final String postalCode;
private final CountryCode country;
}
I have class let's say CheeseMojo having various fields as follow:
public class CheeseMojo {
private String recipies = "Recipies";
private int age;
private String name;
private int submissionId;
//getter/setter
}
Now I want to sort it based on submissionId using Collectors.groupingBy function, then below code snippet will do the job
Map<Integer,List<CheeseMojo>> map = new HashMap<>();
map = cheeseMojos.stream().collect(Collectors.groupingBy(CheeseMojo::getSubmissionId));
And the output will be something like below:
{1=[CheeseMojo#111111],2=[CheeseMojo#222222]}
But I want my output something like below:
{"Recipies1"=[CheeseMojo#111111],"Recipies2"=[CheeseMojo#222222]}
Please help
Why don't you just map the key either before collecting (lets say by creating an instance of a wrapper class that has
public class CheeseMojoWrapper {
private final String id;
private final CheeseMojo wrapped;
public CheeseMojoWrapper(CheeseMojo toWrap) {
id = "Recipe" + toWrap.getId();
wrapped = toWrap;
}
}
or using a stream on the entrySet() of your resulting map...
I am glued with some Jackson polymorphic problem.
I work on a web JDR Character Editor personnal project. I use Springboot and try to stuck with the phylosophy. Moreover, I try to make some independent packages, because of study-case for my real work (another springboot project).
With no Jackson configuration, I have no problem for serialization of a Competence. But when I try to get back any modification on the web editor, so when Jackson make a deserialization of a Competence, problems occur with "dependance" property.
Here are my classes:
The one I try to serialize/deserialize:
public class Competence implements Composante, ComposanteTemplate {
public enum Categorie {
APPRENTI,
COMPAGNON
}
private String nom;
private String description;
private Categorie categorie;
private Chapitre chapitre;
private AttributTemplate dependance;
private List sousCompetences = new ArrayList();
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Competence getTemplate() {
return this;
}
public Categorie getCategorie() {
return categorie;
}
public void setCategorie(Categorie categorie) {
this.categorie = categorie;
}
public Chapitre getChapitre() {
return chapitre;
}
public void setChapitre(Chapitre chapitre) {
this.chapitre = chapitre;
}
public AttributTemplate getDependance() {
return dependance;
}
public void setDependance(AttributTemplate dependance) {
this.dependance = dependance;
}
public List getSousCompetences() {
return sousCompetences;
}
public void setSousCompetences(List sousCompetences) {
this.sousCompetences = sousCompetences;
}
public boolean isOuverte() {
return !sousCompetences.isEmpty();
}
}
The superclass of the property I have a problem with:
public interface AttributTemplate extends ComposanteTemplate {}
The two subclasses which could be use for Competence#dependance property:
public enum Carac implements AttributTemplate, Attribut {
FORT(Type.PHYSIQUE),
AGILE(Type.PHYSIQUE),
RESISTANT(Type.PHYSIQUE),
OBSERVATEUR(Type.PHYSIQUE),
SAVANT(Type.MENTALE),
RUSE(Type.MENTALE),
TALENTUEUX(Type.MENTALE),
CHARMEUR(Type.MENTALE);
public enum Type {
PHYSIQUE,
MENTALE
}
public final Type type;
public final String nom = name().toLowerCase();
private String description;
Carac(Type type) {
this.type = type;
}
#Override
public String getNom() { return nom; }
#Override
public String getDescription() { return description; }
#Override
public Carac getTemplate() { return this; }
public void setDescription(String description) { this.description = description; }
}
public enum ArtTemplate implements AttributTemplate {
ART_GUERRIER(2, 1),
ART_ETRANGE(1, 2),
ART_GUILDIEN(1, 1);
public static final String ART_PREFIX = "ART";
public final String nom = name().toLowerCase().replace("_", " ");
public final int nbCaracsPhysiques;
public final int nbCaracsMentales;
private String description;
ArtTemplate(int nbCaracsPhysiques, int nbCaracsMentales) {
this.nbCaracsMentales = nbCaracsMentales;
this.nbCaracsPhysiques = nbCaracsPhysiques;
}
#Override
public String getNom() {
return nom;
}
#Override
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getNbCaracs() {
return nbCaracsPhysiques + nbCaracsMentales;
}
}
The result json (and then the json I send) is:
{"nom":"Comp_1489746646510","description":"ezbuixnwrclfvmgwdviubcauenzytpzzvumnohwyhpuynxaqhkjdbqygtrmbtlschthovuyoiolkauucwokkfjnaujnufshrjboykuqce","categorie":"APPRENTI","chapitre":"GUERRE","dependance":"ART_ETRANGE","ouverte":false,"sousCompetences":[]}
QUESTION:
I understand that my problem is caused by the abstract relation AttributTemplate, and then when Jackson try to deserialize, he does not know which of Carac or ArtTemplate class to use.
I try to keep unchanged Competence (Competence come from an external jar), so no annotation on this class is possible.
I've tried many of the solutions I found (Jackson 1.5: Polymorphic Type Handling, first steps ) and the only one which has worked was to define a DeserializationProblemHandler
mapper.addHandler(new DeserializationProblemHandler() {
#Override
public Object handleMissingInstantiator(DeserializationContext ctxt, Class<?> instClass, JsonParser p, String msg) throws IOException {
if (instClass == AttributTemplate.class) {
String name = p.getText();
return !name.startsWith(ArtTemplate.ART_PREFIX) ? Carac.valueOf(name) : ArtTemplate.valueOf(name);
}
return super.handleMissingInstantiator(ctxt, instClass, p, msg);
}
});
But I feel bad with this solution, because I am sure there is an other beautiful one.
So is it possible to configure the mapper in order that he is able to determine which of Carac or ArtTemplate he must use to get AttributTemplate?
EDIT:
I managed to have this:
{"nom":"Comp_1489756873433","description":"kruzueemlwisibshlkotasayfkhdqkqolvhlqgsnntndkpvbmmgklqysabiakaolempmupeyiqaztdcrhwimdksgzybbdzttwnwqjxhfo","categorie":"COMPAGNON","chapitre":"GUERRE","dependance":["mova.ged.perso.inne.Carac","AGILE"],"ouverte":true,"sousCompetences":[...]}
by configuring like this the mapper
abstract class CompetenceMixIn {
private AttributTemplate dependance;
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.EXISTING_PROPERTY, property="dependance")
#JsonSubTypes({ #JsonSubTypes.Type(value = Carac.class, name = "carac"), #JsonSubTypes.Type(value = ArtTemplate.class, name = "artTemplate") })
public void setDependance(AttributTemplate dependance) {
this.dependance = dependance;
}
}
ObjectMapper mapper = jsonConverter.getObjectMapper();
mapper.addMixIn(Competence.class, CompetenceMixIn.class);
As you could see, I'm still parasited with the array that wrapped dependance value. I would (...)"dependance": "AGILE", (...) not (...)"dependance":["mova.ged.perso.inne.Carac", "AGILE"], (...)
And I don't know what to change in order to have this.
i have been looking into what you are trying to do. Unfortunatelly, I believe there are issues with Enums + inheritance.
I have an alternative solution that you could be using which is to use a custom creator and ignore unknown properties. See the following example:
public class JacksonInheritance {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Competence c = new Competence();
c.desc = "desc";
c.nome = "nome";
c.template = Att1.TEST_Att1;
String test = mapper.writeValueAsString(c);
System.out.println(test);
Competence readValue = mapper.readValue(test, Competence.class);
System.out.println(readValue.template);
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Competence {
private static final Map<String, AttributeTemplate> templates;
static {
templates = new HashMap<>();
Stream.of(Att1.values()).forEach( a -> templates.put(a.name(), a));
Stream.of(Att2.values()).forEach( a -> templates.put(a.name(), a));
}
#JsonProperty
String nome;
#JsonProperty
String desc;
#JsonIgnore
AttributeTemplate template;
#JsonProperty("template_type")
public String getTempl() {
// Here you can do whichever way uou would like to serialise your template. This will be the key
return template.toString();
}
#JsonCreator
public static Competence create(#JsonProperty("template_type") String templateType) {
Competence c = new Competence();
c.template = templates.get(templateType);
return c;
}
}
public static interface AttributeTemplate {
}
public static enum Att1 implements AttributeTemplate {
TEST_Att1;
}
public static enum Att2 implements AttributeTemplate {
TEST2_Att2;
}
}
Here I am detaching the enum logic from the jackson logic and implement my own. This does not require a custom serialisation.
I basically say that I serialise my enum as its value (you can obviously choose which ever properties you would like for this).
My output json then looks as:
{"template_type":"TEST_Att1","nome":"nome","desc":"desc"}
At the return step I now know that the information I need to construct the correct enum template type from the template_type attribute. This is what I can inject into my factory method create.
In the create I can use my statically created map to populate the correct enum into my object. We can just create this map statically since our enums are finite and static.
The beauty of this is also that the generator is only used for creation. Using #JsonIgnoreProperties(ignoreUnknown = true), we can tell jackson to not freak out by all our custom elements in the json. It will simply deserialise any fields it can detect and leave the other ones (since we are using a custom template_type for our enum resolution).
Finally, I am ignoring the actual template in my bean because jackson won't be able to construct that.
I hope that this works for you/helps you. Sorry about the delay.
Reason for not using inheritance:
There seem to be issues with enum + inheritance in jackson. Particularly jackson by default uses reflection and calls the private constructor of the enum for generation. You may be able to get creators to work in a similar way as above though.
The deserialisation expects the template. I am going of the assumption that you do NOT necessarily want to serialise all elements of the enum. This is because the enum name, in my case TEST_Att1 makes the enum unique. There is no need to serialise and send all the different attributes these enums have around. However, Deserialization with #JsonSubTypes for no value - missing property error shows that jackson requires your template field to be at least present. This is a a slight issue, because you want to use an external property for this instead (so why include a null-field as suggested in your json just to make jackson happy)
This may not be the best solution, but I think it is relatively elegant given the restrictions. I hope that helps you,
Artur
I have two types of payload coming from upstream: It's either PayloadA or PayloadB. There are some common fields between PayloadA and PayloadB so I created Payload class with those common fields and for rest I created two builder class one for each payload.
Below is the builder class for PayloadA:
public final class PayloadA {
private final String clientId;
private final String langid;
private final String deviceId;
private final Map<String, String> applicationPayload;
private PayloadA(Builder builder) {
this.clientId = builder.clientId;
this.langid = builder.langid;
this.deviceId = builder.deviceId;
this.applicationPayload = builder.applicationPayload.build();
}
public static class Builder {
protected final String deviceId;
protected String clientId;
protected String langid;
protected ImmutableMap.Builder<String, String> applicationPayload = ImmutableMap.builder();
public Builder(String deviceId) {
this.deviceId = deviceId;
}
public Builder setClientId(String clientId) {
this.clientId = clientId;
return this;
}
public Builder setLangid(String langid) {
this.langid = langid;
return this;
}
public Builder setPayload(Map<String, String> payload) {
this.applicationPayload.putAll(payload);
return this;
}
public PayloadA build() {
return new PayloadA(this);
}
}
// getters and to string here
}
Now below is the class for PayloadB:
public final class PayloadB {
private final String clientid;
private final String type;
private final String payId;
private PayloadB(Builder builder) {
this.clientid = builder.clientid;
this.type = builder.type;
this.payId = builder.payId;
}
public static class Builder {
protected final String type;
protected String payId;
protected String clientid;
public Builder(String type) {
this.type = type;
}
public Builder setPayId(String payId) {
this.payId = payId;
return this;
}
public Builder setClientId(String clientid) {
this.clientid = clientid;
return this;
}
public PayloadB build() {
return new PayloadB(this);
}
}
// getters and to string here
}
Now I have created another class which is Payload class (does this have to be abstract class?) in which I have all the common fields both for PayloadA and PayloadB so I have to set these fields as well somehow and I am not sure how to use below class:
public abstract class Payload {
private long createTimestamp;
private String partition;
private String key;
// some other fields here
// getters and setters here
}
Question:
Now let's say if we get PayloadB from upstream, then I want key field in the Payload class to be whatever is the value of type in PayloadB class in all lower case and if we get PayloadA from upstream, then I want key to be world.
And also if we get PayloadB from upstream and if clientId was set, then I want partition to be 15 and if we get PayloadA from upstream and if clientId was set then I want partition to be 15 but if it was not set and langId was there, then I want partition to be 17.
And I want to set createTimestamp as well which I have to do after building a Payload object. So for example I have build PayloadA object and it will be passed to some other class and there I need to set createTimestamp value on PayloadA object. Not sure how to do that as well? Do I have to clone something?
How can I use Payload class in my builder pattern? I will get two different payloads and there will be few things common in them so common fields I have separated them out in an abstract class.
Should I have on big builder pattern class with everything in it or multiple builder pattern extending something?
I won't pass builder instance to the PayloadX constructor. Either pass values as individual constructor arguments or call setters.
You can define Payload.Builder which would hold common fields of PayloadA and PayloadB. This class will be an abstract class declaring an abstract build method.
PayloadA.Builder and PayloadB.Builder will extend Payload.Builder, implementing the build method.
In this build method you implement whatever custom logic you need to create and set the fields of the PayloadX.
It seems like you want to make your class immutable (careful with applicationPayload by the way). In this case you can't really "set" anything. You can only produce a new instance. There are many ways to do this, for instance you can implement PayloadX withTimestamp(...) method. Or you can extend your build to accept PayloadX and set timestamp there, resulting in something like new PayloadX.Builder(payloadXInstance).setTimestamp(...).build().
I had a static class that contained several static variables:
public class A{
static {
}
public static final String param1= "paramVal1";
public static final String param2= "paramVal2";
}
I want to change the code, so that the variables will be in a map:
public class A{
static {
}
private static Map<String, String> params = new HashMap<String, String>() ;
public static void initParams() {
params.put("param1", paramVal1);
params.put("param2", paramVal2);
}
However, I already have many classes that call those public parameters, and I don't want to go to every class and change it. Is there any way to use some define function, that would cause java to return the map's value, when the parameter is called? i.e if someone calls A.param1, it would return params.get("param1")
A parameter in not as easy to use (and overwrite) than a method. So I think that short answer to your question is no. That's one of the reasons of getters and setters. But you can allways do the inverse, that is keep the old parameters for compatibility and use a map for newer uses :
public class A{
static {
param1 = "paramVal1";
param2 = "paramVal2";
params = new HashMap<String, String>;
params.put("param1", param1);
params.put("param2", param2);
// eventually other inits for params
}
public static final String param1= "paramVal1";
public static final String param2= "paramVal2";
public static Map<String, String params;
/* or better private static Map<String,String> params
and access via getter */
public static getParam(String name) {
return param.get(name);
}
}
That way, old classes could allways do A.param1, and for newer classes you could start using A.get("param1").
You can do this:
public class A {
private static Map<String, String> params = new HashMap<String, String>();
static {
params.put("param1", "paramVal1");
params.put("param2", "paramVal2");
}
public static final String param1 = params.get("param1");
public static final String param2 = params.get("param2");
}