Reputation: 9106
Say I have a class:
public class Person {
String name;
Int age;
}
and a list of objects of this class:
List<Person> people = ...
Normally, running this through a serializer such as Jackson or Gson would result in this:
"[{'name':'John','age':42},{'name':'Sam','age':43}]
but I am looking to serialize to a single json object where each property is a list containing the attributes, like this:
"{'name':['John','Sam'],'age':[42,43]}"
Do any of the serialization libraries support this?
Upvotes: 1
Views: 331
Reputation: 1787
Serialization libraries are generally not designed for stuff like that. What you're looking for is JSON tree transformation and you can easily implement it in both Gson and Jackson.
Here is a transformation example for Gson:
final class Transformations {
private Transformations() {
}
static JsonObject transposeShallow(final Iterable<? extends JsonElement> inArray) {
final JsonObject outObject = new JsonObject();
for ( final String name : scanAllNames(inArray) ) {
final JsonArray outSubArray = new JsonArray();
for ( final JsonElement inJsonElement : inArray ) {
if ( !inJsonElement.isJsonObject() ) {
throw new IllegalArgumentException(inJsonElement + " is not a JSON object");
}
outSubArray.add(inJsonElement.getAsJsonObject().get(name));
}
outObject.add(name, outSubArray);
}
return outObject;
}
private static Iterable<String> scanAllNames(final Iterable<? extends JsonElement> jsonArray) {
final ImmutableSet.Builder<String> allNames = new ImmutableSet.Builder<>();
for ( final JsonElement jsonElement : jsonArray ) {
if ( !jsonElement.isJsonObject() ) {
throw new IllegalArgumentException(jsonElement + " is not a JSON object");
}
allNames.addAll(jsonElement.getAsJsonObject().keySet());
}
return allNames.build();
}
}
The transformations above can be incorporated into serialization process, but this would affect the structure for your objects (e.g. how to indicate transposable collections for root objects and their fields? how to introduce a new "transposing" type and incorporate it in your data model type system? how to deserialize it properly if necessary?).
Once you get a JSON tree, you can transpose it on any nesting level. Transposing the root element is the simplest way as long as you don't need to transform nested elements.
private static final Type peopleType = new TypeToken<Collection<Person>>() {}.getType();
public static void main(final String... args) {
System.out.println(gson.toJson(people, peopleType));
System.out.println(gson.toJson(Transformations.transposeShallow(gson.toJsonTree(people, peopleType).getAsJsonArray())));
}
that gives:
[{"name":"John","age":42},{"name":"Sam","age":43}]
{"name":["John","Sam"],"age":[42,43]}
The solution above can work with almost any types in your code, but is has a small penalty for building a JSON tree.
Something like PersonWrapper
as suggested by Schred might be another option but this requires wrappers for all your types and they need to be updated once your wrapped classes change.
Also, you might also be interested in libraries like Jolt that are designed for JSON transformations (not sure if it's doable in Jolt though).
Upvotes: 0
Reputation: 111
Transform your List<Person>
to new object.
class NewClass {
List<String> name;
List<Integer> ages;
}
Then pass this object through the Serializer to get:
"{'name':['John','Sam'],'age':[42,43]}"
Upvotes: 0
Reputation: 1579
I'd create a sort of "wrapper" that takes in any amount of persons and stores the fields in a way that let them be serialized that way. So in this case, you would create a series of persons, create a wrapper containing those persons and then serialize that.
public class PersonWrapper {
private int[] ages;
private String[] names;
public PersonWrapper(Person... persons) {
ages = new int[persons.length];
names = new String[persons.length];
for (int i = 0; i < persons.length; i++) {
ages[i] = persons[i].getAge();
names[i] = persons[i].getName();
}
}
}
Upvotes: 1