Because it's a bad habit to leave raw types like List<String> on their own in the application I decided to encapsulate it using the following class:
public class EncapsulatedList {
#JsonProperty
private List<String> someWords;
/*
Some setters, getters and so on
*/
}
But it's serialized to:
{
"someWords": [
"cheese",
"random cheese",
"more random cheese"
]
}
It would be a lot nicer to have it as a plain list like:
[
"cheese",
"random cheese",
"more random cheese"
]
Is there a clean way to achieve this using Jackson 2 without having to do this explicitly like deserializing the list first and putting it into the encapsulating class?
From the JavaDoc of #JsonUnwrapped:
Also note that annotation only applies if
Value is serialized as JSON Object (can not unwrap JSON arrays using this mechanism)
If you don't want to use a String[] or List<String> directly then you can always deserialize the type yourself, like:
class EncapsulatedListDeserializer extends StdDeserializer<EncapsulatedList> {
// ctor omitted
public EncapsulatedList deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
List<String> list = new ArrayList<>();
if (node.isArray()) {
for (JsonNode value : node) {
list.add(value.asText());
}
}
return new EncapsulatedList(list);
}
}
At least that somewhat abstracts the deserialization and calling the setter manually.
Related
So I have this JSON structure I'm getting as a HTTP response. Structure looks like this:
{
"field1": "value1",
"field2": {
"field2-2": [
{
"field2-2-1": "some value",
"field2-2-2": {
"key" : "some value"
}
}
]
}
}
Of course I simplified it but you get the idea. Now I use Gson to convert it into a HashMap:
HashMap<String, Object> resultMap = new Gson().fromJson(httpResult, type);
Now, to get to the "field2-2-2" in Java I do this:
LinkedTreeMap someMap = (LinkedTreeMap) resultMap.get("field2");
ArrayList<LinkedTreeMap> someList = (ArrayList) someMap.get("field2-2");
LinkedTreeMap anotherMap = someList.get(0);
anotherMap.get("key");
Once again I simplified it, but is there any better way to access this "deep" field? Or for the sake of readability can I chain methods somehow? What I mean is if this all have been ArrayLists I could do something like:
ArrayList<Arraylist<String>> sampleList = new ArrayList<>();
sampleList.get(0).get(0);
You could directly map it to POJO classes. Like:
#JsonInclude(Include.NON_NULL)
public class ApiResponse {
String field01;
Field2Class field02;
}
Field2Class.class
public class Field2Class {
ArrayList<Field02_2> field02_2;
}
Field02_2.class
public class Field02_2 {
String field02_2_1, field02_2_2;
}
With each class having getters, setters and default constructors.
I have a relatively complex object which contains a number of fields. I need to serialize one of the fields using a custom serializer, but need to emulate the #JsonUnwrapped functionality.
For simplicity's sake I'll cut this down to two fields:
public class MyClass
{
#JsonProperty("subject")
private final String subject;
#JsonSerialize(using=MySenderSerializer.class)
private final MailActor sender;
}
and my custom serializer class is as follows:
public class MySenderSerializer extends StdSerializer<MailActor>
{
public MySenderSerializer()
{
super(MailActor.class, true);
}
#Override
public void serialize(final MailActor value, final JsonGenerator gen, final SerializerProvider provider) throws IOException
{
gen.writeStringField("from_name", value.getName());
gen.writeStringField("from_email", value.getAddress());
}
}
All of this is fine, except that the output JSON looks like this:
{
...
"subject": "test subject",
"sender": {
"from_name": "test from",
"from_email": "test#test.com"
},
...
}
and I need to unwrap the sender field so that the JSON looks like this:
{
...
"subject": "test subject",
"from_name": "test from",
"from_email": "test#test.com",
...
}
If I was using standard serializers I could use the #JsonUnwrapped annotation to do this, but it doesn't appear to be compatible with custom serializers. How can I obtain the required JSON output without writing a custom serializer for the MyClass object?
I have had to look for an alternative to #JsonUnwrapped too, as it was causing me some unrelated issues with this question.
The solution I have implemented would apply to your case similarly, using #JsonAnyGetter. In your MyClass ignore the attribute that needs the special serialization and instead add it with the JsonAnyGetter:
public class MyClass
{
#JsonProperty("subject")
private final String subject;
#JsonIgnore
private final MailActor sender;
#JsonAnyGetter
public Map<String, Object> serializeActor() {
return sender.serializeToMap();
// Of course, here you could create an empty map
// and add the properties of many different classes.
}
}
Then in the MailActor class you implement that method which will return a map with the properties that you may want.
public Map<String, Object> serializeToMap() {
final Map<String, Object> properties = new ArrayMap<>();
properties.put("from_name", this.getName());
properties.put("from_email", this.getAddress());
return properties;
}
The problem of going this way is deserializing the object, since the #JsonAnySetter doesn't get the map with the JSON in a map the same way you give it with the example above. In your case it's even more difficult, as your class' attributes are final. You'd need to use a #JsonCreator which would have to create all your attributes. Something along the lines of:
#JsonCreator
public MyClass( Map< String, String > json ) {
if (json.containsKey("subject")) {
subject = json.get("subject");
} else {
subject = "";
}
String name, address;
if (json.containsKey("from_name")) {
name = json.get("from_name");
} else {
name = "";
}
if (json.containsKey("from_email")) {
address = json.get("from_email");
} else {
address = "";
}
sender = new MailActor(name, address);
}
It's been a while since you posted the question, hopefully this is helpful in some way to someone coming to look for alternatives.
Well, that is because you have designed it to be that way, when jackson maps an object it will map the inner properties as sub-properties of that object, if you want it to serialize those two fields as if they were members of MyClass instead of MailActor, then declare them as such.
This may point out that your object design may have some small flaws.
I would write a custome serializer for the MyClass object but still, in the long run is not a viable solution.
Is it possible to deserialize JSON using Jackson into one of two types based on the content of the JSON?
For example, I have the following Java (technically Groovy, but that's not important) interfaces and classes:
interface Id {
Thing toThing()
}
class NaturalId implements Id {
final String packageId
final String thingId
Thing toThing() {
new PackageIdentifiedThing(packageId, thingId)
}
}
class AlternateId implements Id {
final String key
Thing toThing() {
new AlternatelyIdentifiedThing(key)
}
}
The JSON I will receive will look like either of the following:
This JSON should map to NaturalId {"packageId": "SomePackage", "thingId": "SomeEntity"}
This JSON should map to AlternateId {"key": "SomeUniqueKey"}
Does anyone know how I can accomplish this with Jackson 2.x WITHOUT including type id's?
Are these the only two classes that implement Id? If so, you could write an IdDeserializer class and put #JsonDeserialize(using = IdDeserializer.class) on the Id interface, and the deserializer would look at the JSON and determine which object to deserialize into.
EDIT: The JsonParser is streaming so it should look something like this:
public Id deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
ObjectNode node = jp.readValueAsTree();
Class<? extends Id> concreteType = determineConcreteType(node); //Implement
return jp.getCodec().treeToValue(node, concreteType);
}
Annotate your methods with #JsonIgnore
#JsonIgnore
Thing toThing() {
new PackageIdentifiedThing(packageId, thingId)
}
With Jackson2, you can easily marshall to different classes using generics:
private <T> T json2Object(String jsonString, String type, Class<T> clazz) {
JsonNode jsonObjectNode = getChildNode(jsonString, type);
T typeObject = null;
try {
typeObject = jacksonMapper.treeToValue(jsonObjectNode, clazz);
} catch (JsonProcessingException jsonProcessingException) {
LOGGER.severe(jsonProcessingException);
}
return typeObject;
}
My Controller returns a list of MyObj objects (using #ResponseBody)
public MyObj
{
int a;
int b;
}
The return JSON looks like this:
[{"a":1,"b":2},{"a":2,"b":2}]
I would like to wrap this JSON so it will return something like:
{ "data": [{"a":1,"b":2},{"a":2,"b":2}]}
From what i read i need to enable SerializationConfig.Feature.WRAP_ROOT_VALUE or (?) use
#JsonRootName("data") on top of my controller.
Also tried the #XmlRootElement, nothing seems to work.
Any idea what is the right way to wrap the list of objects with a root name?
It sounds like you're talking about putting #JsonRootName on the list rather than the object, which won't accomplish what you're trying to do.
If you would like to use #JsonRootName you'll want to enable SerializationFeature.WRAP_ROOT_VALUE like you mentioned above and add the annotation to the class:
#JsonRootName("data")
public MyObj {
int a;
int b;
}
This will wrap the objects themselves, not the list:
{
"listName": [
{
"data": {"a":1, "b":2}
},
{
"data": {"a":2, "b":2}
}
]
}
If you want to wrap the list in an object, perhaps creating a generic object wrapper is the best solution. This can be accomplished with a class like this:
public final class JsonObjectWrapper {
private JsonObjectWrapper() {}
public static <E> Map<String, E> withLabel(String label, E wrappedObject) {
return Collections.singletonMap(label, wrappedObject);
}
}
Then before you send your list back with the response, just wrap it in JsonObjectWrapper.withLabel("data", list) and Jackson takes care of the rest.
This should do the job:
List<MyObj> myList;
ObjectWriter ow = mapper.writer()
.with(SerializationFeature.WRAP_ROOT_VALUE)
.withRootName("data");
System.out.println(ow.writeValueAsString(myList));
I know there are many JSON with GSON questions but none of them relate to me directly. My JSON is formatted differently.
I have a JSON data I want to parse using GSON which looks like the following:
[
{
"foo":"1",
"bar":[ { "_id":"bar1"} ],
"too":["mall", "park"]
}
]
And I have the model Classes:
ItemArray Class
public class ItemArray
{
List<Item> itemArray;
//Get set here
}
Item Class
public class Item
{
String foo;
List<Bar> bar;
List<String> too;
//Get set here
}
Bar Class
public class Bar
{
String id;
//Get set here
}
Heres the question. Is the JSON in the correct format? If so, are the model classes in the correct format?
If not, please shove me in the right direction. Thank you in advance!
PS. I can modify the JSON data format if need be.
According to your json, you should simply have :
public class ItemArray extends List<Item> {
}
if you want to keep you java class and change your json it should be :
{
itemArray: [
{
"foo":"1",
"bar":[ { "_id":"bar1"} ],
"too":["mall", "park"]
}
]
}
Oh, and there is a mismatch with the id and _id for Bar :
public class Bar
{
String _id;
//Get set here
}
You could also use an annotation to change the field's name during Json de/serialization.
And last but not least, consider typing your values better. Don't see any data as strings if they are not, you will not a lot of processing in java code to convert things. For instance :
"foo" : 1,
and see foo as an int data member, not a String.
Some times we get JsonArray [ {..} , {..} ] as a response (without 'itemArray' name like yours)
In that case you can use following code
Type fooType = new TypeToken<ArrayList<Item>>() {}.getType();
List<Item> array = new Gson().fromJson(response, fooType);
find more about this Official doc - Gson Array-Examples
If you have a JsonArray like [ {..} , {..} ] you can do this with Gson:
Item[] items = gson.fromJson(json, Item[].class);
To check Json is valid use this tool http://jsonlint.com/
Class Bar(
private String _id;
//create getter/setters
{
public class Item
{
String foo;
List<Bar> bar;
List<String> too;
//Get set here
}
//this is also fine
public class ItemList
{
List<Item> itemArray;
//Get set here
}
you named of list of items "itemArray", but in your json you have not named the corresponding array of items "itemArray".
So make it itemArray, The problem is not in your json, it is valid. Problem is in its representation for Gson,
Gson map keys of json on the variables of object (i.e Java POJO) with same name.
If the name of your list is Class is
List<Item> itemArray;
then the corresponding json array name should also be itemArray, take a look blow
{
itemArray: [
{
"foo":"1",
"bar":[ { "_id":"bar1"} ],
"too":["mall", "park"]
}
]
}
so you can convert json into object like that
Reader reader = new InputStreamReader(IOUtils.toInputStream(json_string));
ItemList itemList = json.toObject(reader, ItemList.class);
Take a look into blow reference for more details
https://stackoverflow.com/questions/13625206/how-to-parse-the-result-in-java/13625567#13625567