I'm using the new firebase sdk for android and use the real database feature. When i use the getValue(simple.class) everything is fine. But when i want to parse a class which is a subclass, all the attribute of the mother class are null, and i have this type of error:
No setter/field for name found on class uk.edume.edumeapp.TestChild
public class TestChild extends TestMother {
private String childAttribute;
public String getChildAttribute() {
return childAttribute;
}
}
public class TestMother {
protected String motherAttribute;
protected String getMotherAttribute() {
return motherAttribute;
}
}
this function
snapshot.getValue(TestChild.class);
motherAttribute attribute is null, and I get
No setter/field for motherAttribute found on class uk.edume.edumeapp.TestChild
the Json that i parse is:
{
"childAttribute" : "attribute in child class",
"motherAttribute" : "attribute in mother class"
}
Firebaser here
This is a known bug in some versions of the Firebase Database SDK for Android: our serializer/deserializer only considers properties/fields on the declared class.
Serialization of inherited properties from the base class, is missing in the in releases 9.0 to 9.6 (iirc) of the Firebase Database SDK for Android. It was added back in versions since then.
Workaround
In the meantime you can use Jackson (which the Firebase 2.x SDKs used under the hood) to make the inheritance model work.
Update: here's a snippet of how you can read from JSON into your TestChild:
public class TestParent {
protected String parentAttribute;
public String getParentAttribute() {
return parentAttribute;
}
}
public class TestChild extends TestParent {
private String childAttribute;
public String getChildAttribute() {
return childAttribute;
}
}
You'll note that I made getParentAttribute() public, because only public fields/getters are considered. With that change, this JSON:
{
"childAttribute" : "child",
"parentAttribute" : "parent"
}
Becomes readable with:
ObjectMapper mapper = new ObjectMapper();
GenericTypeIndicator<Map<String,Object>> indicator = new GenericTypeIndicator<Map<String, Object>>() {};
TestChild value = mapper.convertValue(dataSnapshot.getValue(indicator), TestChild.class);
The GenericTypeIndicator is a bit weird, but luckily it's a magic incantation that can be copy/pasted.
This was apparently finally fixed in release 9.6.
Fixed an issue where passing a derived class to DatabaseReference#setValue() did not correctly save the properties from the superclass.
for:
No setter/field for motherAttribute found on class uk.edume.edumeapp.TestChild
put setter for TestChild class:
public class TestMother {
private String motherAttribute;
public String getMotherAttribute() {
return motherAttribute;
}
//set
public void setMotherAttribute(String motherAttribute) {
this.motherAttribute= motherAttribute;
}
}
Check this https://firebase.google.com/support/guides/firebase-android
it says
"If there is an extra property in your JSON that is not in your Java class, you will see this warning in the log files: W/ClassMapper: No setter/field for ignoreThisProperty found on class com.firebase.migrationguide.ChatMessage
"
Blockquote
You can get rid of this warning by putting an #IgnoreExtraProperties annotation on your class. If you want Firebase Database to behave as it did in the 2.x SDK and throw an exception if there are unknown properties, you can put a #ThrowOnExtraProperties annotation on your class.
Blockquote
Related
I'm playing with Quarkus and trying to build a CRUD REST application; I'm trying to get 2 endpoints returning 2 different views of the same entities. Here is an example on how I would have done in Spring + Jackson:
#Entity
public class Car{
public String model;
#ManyToOne( fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
public Owner owner;
// [...]
}
#Entity
public class Owner{
public String name;
// [...]
}
Here it is the important part: now if I were using Jackson I would have create a CarView class:
public class CarView {
public static class Public {};
public static class Private extends Public {};
}
And with that I would have annotated Car.model with #JsonView(CarView.Public.class) and Car.owner with #JsonView(CarView.Private.class) and then just annotate with the same annotations my methods in the REST controller in order to tell Jackson which view I want to use:
#RequestMapping("/car/{id}")
#JsonView(CarView.Public.class)
public Car getPublic(#PathVariable int id) { /*...*/ }
#RequestMapping("/car/private/{id}")
#JsonView(CarView.Private.class)
public Car getPrivate(#PathVariable int id) { /*...*/ }
Can I accomplish the same result using Quarkus & JSON-B?
Quarkus supports usage of JsonViews to manage the serialization/deserialization of request/response.
(Just to let you know, sadly it's not supported (yet) by smallry-openapi implementation, so even if the serialization would work, you'll still see the full model in swagger.)
An example of usage, taken from official guide https://quarkus.io/guides/resteasy-reactive#jsonview-support:
JAX-RS methods can be annotated with #JsonView in order to customize the serialization of the returned POJO, on a per method-basis. This is best explained with an example.
A typical use of #JsonView is to hide certain fields on certain methods. In that vein, let’s define two views:
public class Views {
public static class Public {
}
public static class Private extends Public {
}
}
Let’s assume we have the User POJO on which we want to hide some field during serialization. A simple example of this is:
public class User {
#JsonView(Views.Private.class)
public int id;
#JsonView(Views.Public.class)
public String name;
}
Depending on the JAX-RS method that returns this user, we might want to exclude the id field from serialization - for example you might want an insecure method to not expose this field. The way we can achieve that in RESTEasy Reactive is shown in the following example:
#JsonView(Views.Public.class)
#GET
#Path("/public")
public User userPublic() {
return testUser();
}
#JsonView(Views.Private.class)
#GET
#Path("/private")
public User userPrivate() {
return testUser();
}
When the result the userPublic method is serialized, the id field will not be contained in the response as the Public view does not include it. The result of userPrivate however will include the id as expected when serialized.
Have you checked #JsonbVisibility or "Jsonb adapter" part in
https://javaee.github.io/jsonb-spec/users-guide.html annotation from Jsonb? I am afraid maybe there isn't a solution in Jsonb yet like #JsonView in Jackson. Jsonb adapter is configuration at bean level(you choose the Jsonb instance when you (de)serialize), not at view level.
I want to map the properties that are coming from a back-end server with the correct names. This is my class:
public class Repository {
public String full_name;
}
And I want to use it as fullName not full_name. How to achieve this without any library? Is this even possible? Thnaks!
No, you don't need a library, but you have to add the annotation #SerializedName before each attribute declaration.
For Example:
public class Repository {
#SerializedName("full_name")
public String fullName;
public void setFullName(String fullName){
this.fullName=fullName;
}
public void getFulltName(){
treturn firstName;
}
}
User RoboPOJOGenerator for generating pojo from your json on Android Studio.
Manual Parsing:
JSONObject json=new JSONObject("your_json_string");
Repository repository=new Repository();
repository.setFullName(json.optString("full_name"));
Note : You no need to add #SerializedName in class attribute declaration for manual parsing
Suppose you have this entity:
class Foo{
String propA;
String propB;
}
and you want to serialize for one API like :
{propA: "ola",
propB: "Holla"}
and for another API like :
{fooPropA: "ola",
fooPropB: "Holla"}
How can this be achieved using jackson and using the same entity. Creating 2 different entities is not an option :)
There are several ways in which you can achieve this. You can enable a custom serializer (already covered by #se_vedem), register an annotation introspector which changes the property names for the corresponding class and so on.
However, if you are willing to only add a string prefix to all the property names, then the Jackson property name strategy is probably the best fit. The naming strategy class has the access to the serialized object type information, so you can make a decision whether to change the property name or not.
Here is an example using a custom annotation that defines the prefix:
public class JacksonNameStrategy {
#Retention(RetentionPolicy.RUNTIME)
public static #interface PropertyPrefix {
String value();
}
#PropertyPrefix("foo_")
public static class Foo {
public String propA;
public String propB;
public Foo(String propA, String propB) {
this.propA = propA;
this.propB = propB;
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new MyPropertyNamingStrategyBase());
System.out.println(mapper.writeValueAsString(new Foo("old", "Holla")));
}
private static class MyPropertyNamingStrategyBase extends PropertyNamingStrategy {
#Override
public String nameForField(MapperConfig<?> config,
AnnotatedField field,
String defaultName) {
PropertyPrefix ann = field.getDeclaringClass().getAnnotation(PropertyPrefix.class);
if (ann != null) {
return ann.value() + defaultName;
}
return super.nameForField(config, field, defaultName);
}
}
}
Output:
{"foo_propA":"old","foo_propB":"Holla"}
In your API method you choose between two ObjectMapper instances one with the default naming naming strategy and one with the custom one.
You can achieve this by using modules feature from Jackson.
Basically, each API would have it's own ObjectMapper and they will be configured with different modules. This way you can create 2 serializers for the same class and register them on the appropriate module. More read can be found here http://wiki.fasterxml.com/JacksonFeatureModules
However, be aware that serializers are loaded in a particular order. First it tries to get the annotated ones, if none is found it will try to get those registered from modules. So, for example if you have your class annotated with serializer, then that serializer(FooSerializer) would be chosen instead of the one configured in module(MySecondFooSerializer).
#JsonSerialize(using = FooSerializer.class)
class Foo{
String propA;
String propB;
}
module.addSerializer(Foo.class, new MySecondFooSerializer());
I have an issue (de)serializing JSON that is not defined by me.
Here is some code and JSON that explains the issue:
Code:
public static class Base {
public String klass = "base";
}
public static class SubBase extends Base {
}
public static class Sub1 extends SubBase {
public Sub1() {
klass = "Sub1";
}
}
public static class Sub2 extends SubBase {
public Sub2() {
klass = "Sub2";
}
}
public static class Holder {
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_ARRAY, property="type")
#JsonSubTypes({#JsonSubTypes.Type (name = "sub1", value = Sub1.class),#JsonSubTypes.Type(name = "sub2", value = Sub2.class)})
public List<Base> items = new ArrayList<Base>();
}
Holder holder = new Holder();
holder.items.add(new Sub1());
holder.items.add(new Sub1());
mapper.writeValueAsString(holder);
produces
{"items":[["sub1",{"klass":"Sub1"}],["sub1",{"klass":"Sub1"}]]}
If I change the JsonTypeInfo annotation to
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT, property="type")
produces
{"items":[{"sub1":{"klass":"Sub1"}},{"sub1":{"klass":"Sub1"}}]}
So far, all is good :)
However, the JSON I'm getting from the server has a slightly different structure:
{"type":"sub1", "items":[{"klass":"Sub1"},{"klass":"Sub1"}]}
Where the type of the items array is defined in the "type" property (note that all items in the "items" array are of the same class).
I just cannot figure out which JsonTypeInfo combination to use to make this happen. I tried setting the 'include' to ".EXTERNAL_PROPERTY", but this doesn't work.
Deserializing using this inclusion gives me "Exception in thread "main" org.codehaus.jackson.JsonGenerationException: Can not write a field name, expecting a value" error message.
My question is: Which annotation do I need to use so that '{"type":"sub1", "items":[{"klass":"Sub1"},{"klass":"Sub1"}]}' will fill the 'items' array with all Sub1 instances based on the "type" property of the Holder?
If this is just not possible, is there another way to accomplish this (without the need of an custom serializer for Holder; a custom serializer just for the 'items' array would be fine)?
Thanks!
There is no way to map JSON you show automatically; it is not one of 4 types supported by Jackson.
If you can't make server produce more standard structure (for which automatic support exists), you will need to write a custom deserializer; or to do data-binding in two steps, first into an intermediate easily mappable structure (like JsonNode or Map) and then from that into desired structure manually extracting type, using that (for example, with ObjectMapper.convertValue(object, resultType).
I am getting the following error when trying to get a JSON request and process it:
org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class com.myweb.ApplesDO]: can not instantiate from JSON object (need to add/enable type information?)
Here is the JSON I am trying to send:
{
"applesDO" : [
{
"apple" : "Green Apple"
},
{
"apple" : "Red Apple"
}
]
}
In Controller, I have the following method signature:
#RequestMapping("showApples.do")
public String getApples(#RequestBody final AllApplesDO applesRequest){
// Method Code
}
AllApplesDO is a wrapper of ApplesDO :
public class AllApplesDO {
private List<ApplesDO> applesDO;
public List<ApplesDO> getApplesDO() {
return applesDO;
}
public void setApplesDO(List<ApplesDO> applesDO) {
this.applesDO = applesDO;
}
}
ApplesDO:
public class ApplesDO {
private String apple;
public String getApple() {
return apple;
}
public void setApple(String appl) {
this.apple = apple;
}
public ApplesDO(CustomType custom){
//constructor Code
}
}
I think that Jackson is unable to convert JSON into Java objects for subclasses. Please help with the configuration parameters for Jackson to convert JSON into Java Objects. I am using Spring Framework.
EDIT: Included the major bug that is causing this problem in the above sample class - Please look accepted answer for solution.
So, finally I realized what the problem is. It is not a Jackson configuration issue as I doubted.
Actually the problem was in ApplesDO Class:
public class ApplesDO {
private String apple;
public String getApple() {
return apple;
}
public void setApple(String apple) {
this.apple = apple;
}
public ApplesDO(CustomType custom) {
//constructor Code
}
}
There was a custom constructor defined for the class making it the default constructor. Introducing a dummy constructor has made the error to go away:
public class ApplesDO {
private String apple;
public String getApple() {
return apple;
}
public void setApple(String apple) {
this.apple = apple;
}
public ApplesDO(CustomType custom) {
//constructor Code
}
//Introducing the dummy constructor
public ApplesDO() {
}
}
This happens for these reasons:
your inner class should be defined as static
private static class Condition { //jackson specific
}
It might be that you got no default constructor in your class (UPDATE: This seems not to be the case)
private static class Condition {
private Long id;
public Condition() {
}
// Setters and Getters
}
It could be your Setters are not defined properly or are not visible (e.g. private setter)
I would like to add another solution to this that does not require a dummy constructor. Since dummy constructors are a bit messy and subsequently confusing. We can provide a safe constructor and by annotating the constructor arguments we allow jackson to determine the mapping between constructor parameter and field.
so the following will also work. Note the string inside the annotation must match the field name.
import com.fasterxml.jackson.annotation.JsonProperty;
public class ApplesDO {
private String apple;
public String getApple() {
return apple;
}
public void setApple(String apple) {
this.apple = apple;
}
public ApplesDO(CustomType custom){
//constructor Code
}
public ApplesDO(#JsonProperty("apple")String apple) {
}
}
When I ran into this problem, it was a result of trying to use an inner class to serve as the DO. Construction of the inner class (silently) required an instance of the enclosing class -- which wasn't available to Jackson.
In this case, moving the inner class to its own .java file fixed the problem.
Generally, this error comes because we don’t make default constructor.
But in my case:
The issue was coming only due to I have made used object class inside parent class.
This has wasted my whole day.
Thumb Rule: Add a default constructor for each class you used as a mapping class. You missed this and issue arise!
Simply add default constructor and it should work.
Can you please test this structure. If I remember correct you can use it this way:
{
"applesRequest": {
"applesDO": [
{
"apple": "Green Apple"
},
{
"apple": "Red Apple"
}
]
}
}
Second, please add default constructor to each class it also might help.
You have to create dummy empty constructor in our model class.So while mapping json, it set by setter method.
Regarding the last publication I had the same problem where using Lombok 1.18.* generated the problem.
My solution was to add #NoArgsConstructor (constructor without parameters), since #Data includes by default #RequiredArgsConstructor (Constructor with parameters).
lombok Documentation
https://projectlombok.org/features/all
That would solve the problem:
package example.counter;
import javax.validation.constraints.NotNull;
import lombok.Data;
#Data
#NoArgsConstructor
public class CounterRequest {
#NotNull
private final Integer int1;
#NotNull
private final Integer int2;
}
If you start annotating constructor, you must annotate all fields.
Notice, my Staff.name field is mapped to "ANOTHER_NAME" in JSON string.
String jsonInString="{\"ANOTHER_NAME\":\"John\",\"age\":\"17\"}";
ObjectMapper mapper = new ObjectMapper();
Staff obj = mapper.readValue(jsonInString, Staff.class);
// print to screen
public static class Staff {
public String name;
public Integer age;
public Staff() {
}
//#JsonCreator - don't need this
public Staff(#JsonProperty("ANOTHER_NAME") String n,#JsonProperty("age") Integer a) {
name=n;age=a;
}
}
You must realize what options Jackson has available for deserialization. In Java, method argument names are not present in the compiled code. That's why Jackson can't generally use constructors to create a well-defined object with everything already set.
So, if there is an empty constructor and there are also setters, it uses the empty constructor and setters. If there are no setters, some dark magic (reflections) is used to do it.
If you want to use a constructor with Jackson, you must use the annotations as mentioned by #PiersyP in his answer. You can also use a builder pattern. If you encounter some exceptions, good luck. Error handling in Jackson sucks big time, it's hard to understand that gibberish in error messages.
Add default constructors to all the entity classes
Failing custom jackson Serializers/Deserializers could also be the problem. Though it's not your case, it's worth mentioning.
I faced the same exception and that was the case.
For me, this used to work, but upgrading libraries caused this issue to appear. Problem was having a class like this:
package example.counter;
import javax.validation.constraints.NotNull;
import lombok.Data;
#Data
public class CounterRequest {
#NotNull
private final Integer int1;
#NotNull
private final Integer int2;
}
Using lombok:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
Falling back to
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
Fixed the issue. Not sure why, but wanted to document it for future.