If I have a type "Person", and it has multiple fields, including "password", then how do I tell GSON that I want accept the password field when it's passed in, but not to pass it back out?
Specifically, in this case, it's because my web front end can be used to update the password and send it to the Java side,, but I never want to send the password back to the front end (for obvious security reasons).
I am not sure you can do it with Gson, but you could with Genson. Put #JsonIgnore(deseriaize=true) on your getPassword method.
Or if you want genson to use only fields instead of public getter/setter and fields, configure it like that:
Genson genson = new Genson.Builder()
.setUseGettersAndSetters(false)
.setFieldVisibility(VisibilityFilter.DEFAULT)
.create();
In that case put the annotation on the field.
You can deserialize your class as usual (since you want to deserialize all the fields) and write a custom serializer that excludes the password. Something like this:
public class PersonSerializer implements JsonSerializer<Person> {
#Override
public JsonElement serialize(Person src, Type typeOfSrc, JsonSerializationContext context)
{
JsonObject obj = new JsonObject();
obj.addProperty("name", src.name);
obj.addProperty("gender", src.gender);
obj.addProperty("age", src.age);
//And all the other fields but the password...
return obj;
}
}
Then you just need to register the serializer with:
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(Person.class, new PersonSerializer());
And finally serialize your object as usual with gson.toJson method...
I'm not sure if it's the best approach, but it's pretty straightforward... Otherwise you can take a look at Gson's excusion strategies...
Related
I have a POJO class like:
class Cat {
public Cat() {}
private String name;
private JsonElement arbitraryProperties;
}
I am using Jackson (JAXB) and cat is a resource I want to import and export.
GET /cats/{id}
POST /cats
Export is working fine. Import did not work because JsonElement is abstract. So I added #JsonDeserialize(using = MyJsonElementDeserialize.class) annotation to the arbitraryProperties field.
The MyJsonElementDeserialize class looks like:
public class JsonElementDeserialize extends JsonDeserializer<JsonElement> {
public JsonElementDeserialize() {
}
#Override
public JsonElement deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return new JsonObject(); // here I need to create my object later
}
}
When I now call POST /cats with payload:
{
"name": "Mia",
"arbitraryProperties": {
"a": 3,
"b": [],
"c": {
"d": "race"
}
}
}
Jacksons returns error code 400:
Unrecognized field "a" (class Cat), not marked as ignorable (1 known properties: "name"]).
Where is my mistake? How can I have a JsonElement in my POJO that gets automatically serialized and deserialized?
Here is a simple solution using a setter method in Cat to transform something Jackson parses (e.g. Map<String, Object>) to a GSON JsonElement type:
void setArbitraryProperties(Map<String, Object> properties) {
arbitraryProperties = new Gson().toJsonTree(properties).getAsJsonObject();
}
You can do something similar inside a custom deserializer if you can't use a setter here. I find setters/getters simpler for transformations with Jackson.
Note that a Gson instance is employed to convert the Map to JsonElement (a JsonObject actually). Docs for toJsonTree.
Since you have a field of a type defined in GSON and therefore you have GSON as a dependency and inexorably need GSON to create this field, you may consider Sharon's suggestion and skip Jackson and use GSON for deserialization.
The problem here is that deserializers must consume all content that they cover: that is, if your deserializer does not "read" JSON content that would bind (contents of a here), they are left for further processing.
Solution should be easy, just call
jsonParser.skipChildren();
in deserialize method and it will skip over current input token, as well as all of its contents (if its JSON Object or Array).
Alternatively, if you wanted to use contents, you could use
jsonParser.readValueAsTree();
and there are a few other convenience methods.
If could, I would change JsonElement to JsonNode, and use “Tree Model” of Jackson.
Try to use "produces=MediaType.APPLICATION_JSON_VALUE" and #ResponseBody in controller method if you are using Spring mvc.
I have found many similar questions but none of them has helped.
This works:
public class Assets<T> {
public T getAndDeserializeAsset(String endpoint, String assetId, Client client){
Response response = client.get(endpoint+assetId);
Gson gson = new Gson();
T asset = gson.fromJson(response.body, new TypeToken<Email>(){}.getType());
return asset;
}
}
This doesn't:
public class Assets<T> {
public T getAndDeserializeAsset(String endpoint, String assetId, Client client){
Response response = client.get(endpoint+assetId);
Gson gson = new Gson();
T asset = gson.fromJson(response.body, new TypeToken<T>(){}.getType());
return asset;
}
}
I have made it according to official documentation, so I have no idea why it doesn't work.
Error:
Exception in thread "main" java.lang.ClassCastException: com.rest.api.Response cannot be cast to model.Email
at main.Main.main(Main.java:34)
The only thing that makes sense is that your instance of Assets<T> must be an instance of Assets<Response>, not of Assets<Email>. Are you absolutely sure your instance of Assets is the correct type?
Also, remember that Gson is not covariant; when you specify the type using TypeToken, you must use the exact type you wish to deserialize to. For example, if you use the class definition:
class Email extends Response {
and then use:
new Assets<Response>();
Because Gson isn't covariant, has no way to know that what you really want is an Email.
The above code fails to interpret value as type, because Gson invokes Assets.getClass() to get its class information, but this method returns a raw class, Assets.class. This means that Gson has no way of knowing that this is an object of type Assets< Example>, and not just plain Assets.
You can solve this problem by specifying the correct parameterized type for your generic type. You can do this by using the TypeToken class.
Type AssetsType = new TypeToken<Assets<Example>>() {}.getType();
gson.toJson(assets, AssetsType );
gson.fromJson(json, AssetsType );
for more information check gson-user-guide
Is there any way the set methods of a given class, are used when using Gson's fromJson method?
I would like to do this because for every String global variable of the target class a trim is made.
Is there any GSON API annotation for this?
I am aware that GSON provides the ability to write custom serializers/deserializers but I would like to know if there is another way to achieve this.
No, there is not. Gson works mainly by reflection on instance fields. So if you do not plan to move to Jackson that has this feature I think you cannot have a general way to call your setters. So there's no annotation for that.
BUT
to achieve your specific need you could:
write your own custom TypeAdapter or
create a constructor that has the string you intend to trim and create a custom InstanceCreator or
parse your JSON as JsonObject, do some processing of the strings and then use that object as source for parsing into your class.
I can provide you with more hints as long as you post some code or give information about your data/JSON.
I implemented a JsonDeserializer<String> and registered it on GsonBuilder. So, to all String fields received, Gson will use my StringGsonTypeAdapter to deserialize the value.
Below is my code:
import static net.hugonardo.java.commons.text.StringUtils.normalizeSpace;
import static net.hugonardo.java.commons.text.StringUtils.trimToNull;
final class StringGsonTypeAdapter implements JsonDeserializer<String> {
private static final StringGsonTypeAdapter INSTANCE = new StringGsonTypeAdapter();
static StringGsonTypeAdapter instance() {
return INSTANCE;
}
#Override
public String deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return normalizeSpace(trimToNull(jsonElement.getAsString()));
}
}
...and my GsonBuilder:
Gson gson = new GsonBuilder()
.registerTypeAdapter(String.class, StringGsonTypeAdapter.instance())
.create())
I want to deserialize json objects to specific types of objects (using Gson library) based on type field value, eg.:
[
{
"type": "type1",
"id": "131481204101",
"url": "http://something.com",
"name": "BLAH BLAH",
"icon": "SOME_STRING",
"price": "FREE",
"backgroundUrl": "SOME_STRING"
},
{
....
}
]
So type field will have different (but known) values. Based on that value I need to deserialize that json object to appropriate model object, eg.: Type1Model, Type2Model etc.
I know I can easily do that before deserialization by converting it to JSONArray, iterate through it and resolve which type it should be deserialized to. But I think it's ugly approach and I'm looking for better way. Any suggestions?
You may implement a JsonDeserializer and use it while parsing your Json value to a Java instance. I'll try to show it with a code which is going to give you the idea:
1) Define your custom JsonDeserializer class which creates different instance of classes by incoming json value's id property:
class MyTypeModelDeserializer implements JsonDeserializer<MyBaseTypeModel> {
#Override
public MyBaseTypeModel deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
JsonElement jsonType = jsonObject.get("type");
String type = jsonType.getAsString();
MyBaseTypeModel typeModel = null;
if("type1".equals(type)) {
typeModel = new Type1Model();
} else if("type2".equals(type)) {
typeModel = new Type2Model();
}
// TODO : set properties of type model
return typeModel;
}
}
2) Define a base class for your different instance of java objects:
class MyBaseTypeModel {
private String type;
// TODO : add other shared fields here
}
3) Define your different instance of java objects' classes which extend your base class:
class Type1Model extends MyBaseTypeModel {
// TODO: add specific fields for this class
}
class Type2Model extends MyBaseTypeModel {
// TODO: add specific fields for this class
}
4) Use these classes while parsing your json value to a bean:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyBaseTypeModel.class, new MyTypeModelDeserializer());
Gson gson = gsonBuilder.create();
MyBaseTypeModel myTypeModel = gson.fromJson(myJsonString, MyBaseTypeModel.class);
I can not test it right now but I hope you get the idea. Also this link would be very helpful.
#stephane-k 's answer works, but it is a bit confusing and could be improved upon (see comments to his answer)
Copy https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java into your project. (It's ok; these classes are designed to be copy/pasted https://github.com/google/gson/issues/845#issuecomment-217231315)
Setup model inheritance:
// abstract is optional
abstract class BaseClass {
}
class Type1Model extends BaseClass {
}
class Type2Model extends BaseClass {
}
Setup GSON or update existing GSON:
RuntimeTypeAdapterFactory<BaseClass> typeAdapterFactory = RuntimeTypeAdapterFactory
.of(BaseClass.class, "type")
.registerSubtype(Type1Model.class, "type1")
.registerSubtype(Type2Model.class, "type2");
Gson gson = new GsonBuilder().registerTypeAdapterFactory(typeAdapterFactory)
.create();
Deserialize your JSON into base class:
String jsonString = ...
BaseClass baseInstance = gson.fromJson(jsonString, BaseClass.class);
baseInstance will be instanceof either Type1Model or Type2Model.
From here you can either code to an interface or check instanceof and cast.
use https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
then configure it with
public static final class JsonAdapterFactory extends
RuntimeTypeAdapterFactory<MediumSummaryInfo> {
public JsonAdapterFactory() {
super(MyBaseType.class, "type");
registerSubtype(MySubtype1.class, "type1");
registerSubtype(MySubtype2.class, "type2");
}
}
and add the annotation:
#JsonAdapter(MyBaseType.JsonAdapterFactory.class)
to MyBaseType
Much better.
If you have a lot of sub types and you do not want to or cannot maintain a list of them, you can also use an annotation based approach.
Here is the required code and also some usage examples:
https://gist.github.com/LostMekka/d90ade1fe051732d6b4ac60deea4f9c2
(it is Kotlin, but can easily be ported to Java)
For me, this approach is especially appealing, since I write a small library that does not know all possible sub types at compile time.
I have an object like:
public class Foo{
public String f1="[{\"jsonField\":\"something\"},{\"jsonField\":\"something\"}]";
}
Gson would serialize it to:
{"f1":"[{\"jsonField\":\"something\"},{\"jsonField\":\"something\"}]"}
The f1 field was serialized to a string. Apparently, the field is a well formed json-format string. How can I do to serialized the field to an jso array, like below:
{"f1":[{"jsonField":"something"},{"jsonField":"something"}]}
PS. For performance consideration, I can't deserialize then serialize the field.
Gson is serializing your string field to a string. That's normal. If your field, however, was a List<String>, Gson would give you:
{"f1":["jsonField:something","jsonField:something"]}
And if it was a List where something has a field called jsonField you'd get what you actually want.
This is the implicit way that Gson serializes your objects. If you don't like it, you need to implement your own TypeAdapter and register it with Gson at the builder.
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MyObject.class, new SomethingGsonAdapter());
Gson gson = builder.create()
And you need to define:
public class SomethingGsonAdapter implements JsonDeserializer<DataItem>, JsonSerializer<DataItem>;