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>;
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 a little problem.
I stored some data in a json-file, and it works fine.
But if i try to retrieval this data there is a problem with the interfaces of the object i am trying to create.
This is a simplified version of my class
public class MyObject{
//this is a interface
public Vector3Int position;
...}
Ok, GSON can't create interfaces without a consturctor, so i create this class:
public class VectorInstanceCreator implements InstanceCreator<Vector3Int >{
#Override
public Vector3Int createInstance(Type arg0) {
return new VectorImpl();
}
}
And i use it in this way:
...
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Vector3Int.class, new new VectorInstanceCreator());
Gson gson = gsonBuilder.create();
gson.fromJson(myJsonString, MyObject.class);
...
So, it works but the values in my json file is for example:
"position": {"x": 1.0, "y": 1.0,"z": 0.0}
but each value in the Java-Object-Vector is 0. So it means, gson use just the given consturctor of my VectorInstanceCreator class, but is not setting the values of the JSON-File/Object.
How do i set this values by using a interface with InstanceCreator?
I found a simple solution on http://technology.finra.org/code/serialize-deserialize-interfaces-in-java.html for my problem. With the template InterfaceAdapter, each class name will stored additional in the json file. So the retrieval method, to restore the classes know the exact class and not just the interface. This works well for me.
I am trying to serialize and de-serialize an ArrayList of Java POJOs using Gson on Json objects
I have an object MyClass as
public class MyClass{
private int type
private int pos;
private Object value;
}
I have an ArrayList of these objects, which I serialize as
List<MyClass> values= null;
String json = new Gson().toJson(retValues);
The json string is
[{"type":4,"pos":1,"value":15}]
I try to deserialize it as
Type myType = new TypeToken<ArrayList<MyClass>>() {}.getType();
List<MyClass> test=new Gson().fromJson(json, myType);
I get an error
The JsonDeserializer com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter#1141ddf failed to deserialized json object [{"type":4,"pos":1,"value":18}] given the type java.util.ArrayList<abc.MyClass>
Any input much appreciated!
I figured it out. I added 2 things, I don't know which one made it work.
- I added a no-arguments constructor to MyClass
- I made MyClass implement serializable.
And it works!
Thanks for your help.
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())
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...