Specifically I am wondering how when deserializing an object the deserializer could set a private field? Thinking of an example class like this:
public class MyClass {
#JsonProperty( "My String" );
private String myString;
}
If this is deserialized using objectMapper.readValue(json, MyClass.class); how does the resulting object have this field set if it is marked as private?
Calling Field.setAccessible(true) before reading or writing a value through reflection does the trick here.
For details see the corresponding javadoc: https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-
But use with care ;-)
Quite a few frameworks allow access to private fields in this manner by using Field.setAccessible(true). It allows the application to ignore the Java language visibility rules (i.e. private) and read or change the value via an instance of the Reflection API Field class.
A little more can be found in this question:
Java reflection - impact of setAccessible(true)
The short answer is that it can't normally. We use lombok to generate the getter/setter for the variables, but you can of course write your own. Jackson has the same visibility as your main code does, so a private field cannot be mapped without some public getter/setter OR configuring the object mapper like so... objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);.
It wouldn't be able to serialize that either normally. You can use Lombok #Getter and #Setter on the class level so Jackson can work with myString, or put #JsonAutoDetect(fieldVisibility = Visibility.ANY) at the class level like below.
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class MyClass {
#JsonProperty( "My String" );
private String myString;
}
Related
Consider we are having the following classes
//following classes are present in external lib, we can not modify them.
class A{
private Map mapOfListOfB; // this should have been properly typed Map<String,List<B>>
}
class B{
private int val1;
private String val2;
private C val3; // Class C can be anything but the point here is the same object of C can be used in multiple B objects
//which means we can reuse the reference using #JsonIdentityInfo
}
now, when we use Jackson's objectMapper to serialize and deserialize this Class A,
we would not be able to deserialize because we are not giving any typed info to Jackson so it ends up creating List<LinkedHashMap>
here is one solution that I know, works for class having a collection with a specific class type
eg:
class D{
private Map mapOfB // which should have been Map<String,B>
}
// this can be typed by using jackson's mixin
abstract class DmixIn{
#JsonDeserialize(contentAs = B.class)
Map mapOfB
}
but how we can tell the type which is present in class A to Jackson as we can not pass ParameterizedType to contentAs, it just takes class instance.
I know we can write a custom deserializer to achieve the end result but I am looking for a more readable solution here, like any Jackson annotation or any simple config which we can be set on the property level.
and another issue(which can be because of my awareness) is I even need to maintain deserialization context while writing custom deserializers by using #JsonDeserialize(contentUsing = CustomDeserializer.class) because Class C references are reused and I may need to resolve IDs for this POJO while deserializing
I have an object that in its fields is mandatory that some names have '_' for example local_PC instead of localPC.
The problem I have is that I need it to be local_PC and when a call is made to my app they send that field and I can't change it, but sonar launches me error because it must be localPC
Is there any way I can control it by Jackson?
realizing #jsonproperty only allows me to change the names in the output but not in the input of the controller
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
#AllArgsConstructor
public class Example{
private String local_PC;
}
Use #JsonAlias
#JsonAlias is introduced in Jackson 2.9 release. #JsonAlias defines one or more alternative names for a property to be accepted during deserialization i.e. setting JSON data to Java object. But at the time of serialization i.e. while getting JSON from Java object, only actual logical property name is used and not alias. #JsonAlias
#JsonAlias({"local_PC", "localPC"})
private String local_PC;
I am using jackson to handle JSON (de)-serialization. I have a bunch of classes which are annotated with are essentially objects holding properties and associated getters and setters.
However, I often find that at some point I want to add additional properties which I don't want to include in the (de)-serialization process. This does work using #JsonIgnore, but it strikes me as relatively ugly since I have to add the annotation everywhere and things break down as soon as I forget.
I would like to know if there is a better way to separate the ignored and serialized properties. I have the following two ideas:
Use inheritance, add the new properties to the inherited class:
// everything here should be (de)-serialized
class Base {
public int getJSONProperty() {...}
}
// nothing specific to the class Derived should be (de)-serialized
class Derived extends Base {
// *not* to be included
public SomeClass getAdditionalProperty() {...}
}
However, I don't know how to tell jackson to deserialize the Derived
objects as Bases. Is this possible (Does jackson make guarantees
regarding (non)-polymorphic serialization of classes)?
Use MixIn annotations. This would require an additional abstract
class for each existing class. Also I am not sure whether this solves
the problem. Are getters which do not appear in the MixIn base class
ignored automatically or do I need to #JsonIgnore them manually?
I've seen that you don't like the previous solution i've provided, so I'm again here to provide another way to do what you want using Gson Library. I hope to help you this time.
This is The Base Class that you want to serialize
public class Base {
public int getJSONProperty() {
return jsonProperty;
}
private int jsonProperty = 2;
}
This is The Derived Class that you don't want to serialize
public class Derived extends Base{
public String getAdditionalProperty(){
return additionalProperty;
}
private String additionalProperty = "value-not-to-serialize";
}
Using Type type = new TypeToken<Base>(){}.getType(); you can define the class to use for serialization so you get the JSON String using:
Derived derived = new Derived();
Gson gson = new Gson();
Type type = new TypeToken<Base>(){}.getType();
String jsonString = gson.toJson(derived, type);
You know Gson? it's a good library to handle JSON.
You can use transient keyword to define variable thats not must be serialized, this works with Gson (It should work well with jackson, but i'm not sure)...
class Base {
// (de)-serialized
private int jsonProperty;
// not (de)-serialized
private transient SomeClass additionalProperty;
}
I think the best approach is to add annotation or use the transient variable.
Create the inheritance only for the purpose of serialization an object complicates the application uselessly in my point of view...
So I am just trying out Jersey for REST services and it seems to we working out fine. I only expose get services and all of the object types that I expose with these services have an immutable object representation in Java. By default Jersey seems to use a parser (JAXB?), requiring a #XmlRootElement annotation for the class that should be parsed, zero-arg constructor and setters.
I have been using Gson with no zero-arg constructor, no setters and final on all fields with no problems at all. Is there any way to accomplish this with Jersey(i.e. the paser it is using)? I have seen solutions with adapter classes that map data from a immutable object to a mutable representation, but this seems like a lot of boilerplate(new classes, more annotations, etc.) if it can be achieved with Gson without anything added.
Note: 1) I have heard people promote using zero-arg constructor and claim that Gson should not work without it. This is not what I am interested in. 2) I really have tried googling this but my keywords might be off. In other words, humiliate me in moderation.
EDIT 1:
My webservice works if I do like this:
#XmlRootElement
public class Code{
private String code; //Silly object just used for example.
public Code(){}
//(G || S)etters
}
With this class exposing the object:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Set<Code> get(#QueryParam("name") String name) { // Here I want to use a class of my own instead of String name, haven't figured out how yet.
return this.codeService.get(name);
}
If I replace the Code with the following, the webservice stops working:
public class Code{
private final String code;
#JsonCreator
public Code(#JsonProperty("code") String code) {
this.code = code;
}
//Getters omitted
}
What I want is to be able to 1) have immutable objects that can be parsed to/from json and 2) Be able to define something like #RequestBody in Spring MVC for my incoming objects.
Actually this could be pretty easy with Genson. You just need the jar and then configure the Genson feature to use constructors with arguments (if you don't want to put annotations on it).
Genson genson = new GensonBuilder().useConstructorWithArguments(true).create();
// and then register it with jersey
new ResourceConfig().register(new GensonJaxRSFeature().use(genson));
Or you can use JsonProperty on the arguments. See the User Guide for more details.
I'm currently trying to deserialize an API result, which looks like the following
[{"name":"MyName","value":"MyValue"},{"name":"MyName2","value":"MyValue2"}]
ArrayList<BasicNameValuePair> entities = JsonUtils.getObjectMapper()
.readValue(receivedData.toString(),
new TypeReference<ArrayList<BasicNameValuePair>>() {});
Then the following exceptions occurs
Exception mapping result.
No suitable constructor found for type...
Since this is an internal class from org.apache.http.message.BasicNameValuePair, I can not annotate or edit it in any way. But I see (from other android projects) a lot of people using this class. Is there some way to get this working? Serializing to String from BasicNameValuePair works.
Jackson uses reflection to create an instance of your class. By default, it expects a no-arg constructor. The BasicNameValuePair class does not have such a constructor. It has a constructor with two parameters, one for name and one for value.
Typically, if you had control of the class, you could annotate the constructor parameters with #JsonProperty so that Jackson used that constructor instead of the no-arg constructor. Since you don't have control of the code, use Mixins.
Declare a class like so
public static abstract class BasicNameValuePairMixIn {
private BasicNameValuePairMixIn(#JsonProperty("name") String name, #JsonProperty("value") String value) { }
}
And configure your ObjectMapper like so
// configuration for Jackson/fasterxml
objectMapper.addMixInAnnotations(BasicNameValuePair.class, BasicNameValuePairMixIn.class);
Jackson will now use the mixin as a template for your class.
If you are using the older version of Jackson, use the configuration as described here.
Try not to use reserve keywords in your parameter names and then try again. "name","value"