1

I have got a Class PagedResult. The class is there to help me realize a JSON output with different objects in a pages format. The E is the object, that is wrapped in the List. It works all fine, but one thing still bothers me. I would like that the list with the objects does not always get the same name. I would like to adapt the name to the corresponding objects.

Class PagedResult:

public class PagedResult<E> {

Long totalItems;
Integer totalPages;
Integer currentPage;
List<E> elements;

[... Getter & Setter ...] 
}

The actual JSON Output with an Object like MyPojo looks like this:

{
"totalItems": 2,
"totalPages": 1,
"currentPage": 1,
"elements": [
    {
        "myPojoAttr1": "hello",
        "myPojoAttr2": "there"
    },
    {
        "myPojoAttr1": "hello",
        "myPojoAttr2": "folks"
    }
 ]
}

So for each response, no matter which objects, the array is namend as "elements". I don´t want the ugly name in my JSON response, because of the changing objects in the PagedResult-class. When I get a response with objects like MyPojo the name of the JSON-Array should be "myPojos" and when I want to get a response with objects like MyWin the name "myWins".

I tried alot with @JsonProperty, but I can´t find a way, to do this "object-array-name" also generic. Can someone assist me with the problem please? Thanks in advance.

Phiilschke
  • 73
  • 9
  • why do you want a name that doesn't correspond with what's in your class, and if you want another name, why not change the name of the element in your class? – Stultuske Feb 22 '21 at 08:43
  • because the `PagedResult`-class should be able to wrap different objects. I don´t want to create a `PagedResult`-class for all different objects, e.g. `PagedResultMyPojo` or `PagedResultMyWin` because of duplicated code. – Phiilschke Feb 22 '21 at 08:47
  • 1
    in the context of your PagedResult, no matter what type they are, they are 'elements' – Stultuske Feb 22 '21 at 08:52
  • Yes. And that's exactly what I'd like to get around with a generic solution. – Phiilschke Feb 22 '21 at 08:54
  • Use a map instead? You can't use generics to create a class from a string name. You would need to use a bytecode generation library, which I think is overkill in your case. – m0skit0 Feb 22 '21 at 09:27
  • You cannot rename declared variables in Java. Period. – Koenigsberg Feb 22 '21 at 10:10
  • [This](https://stackoverflow.com/questions/55684724/how-to-use-dynamic-property-names-for-a-json-object) may be helpful. – MC Emperor Feb 22 '21 at 10:13

3 Answers3

2

No. You can't do that. Generic types have parameters for types, not for identifiers. AFAIK, nothing in the Java language allows you to treat a Java identifier as a parameter when producing a type. (Certainly, nothing you could use in this context!)

Alternatives:

  • Don't do it. (Take a good hard look at your reasons for wanting the JSON attribute name to vary. What does it actually achieve? Is it worth the effort?)

  • Don't use a generic type. Define a different class for each kind of "paged result". (Clunky. Not recommended.)

  • Use a map, and populate it with a different map key for the elements attribute of each kind of "paged result". (The disadvantage is that you lose static type checking, and take a small performance and storage penalty. But these are unlikely to be significant.)

  • Write a custom mapper to serialize and deserialize the PagedResult as per your requirements.


For what it is worth, identifiers as parameters is the kind of thing you could do with a macro pre-processor. That Java language doesn't have standard support for that kind of thing.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

Yes it's possible, using custom serializers. But even with a custom serializer you still have a problem: Generics are removed at compile time. So we need to somehow get the type during runtime.

Here is an example that will just check the type of the first element in the elements list. Definietly not the cleanest way to do it, but you don't have to adjust your PagedResult class.

public class PagedResultSerializer<T> extends JsonSerializer<PagedResult<Object>> {

    @Override
    public void serialize(PagedResult<Object> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        gen.writeNumberField("totalItems", value.getTotalItems());
        // Your other attributes
        if (!value.getElements().isEmpty()) {
            Object firstElement = value.getElements().get(0);
            String elementsFieldName;
            if (firstElement instanceof MyPojo) {
                elementsFieldName = "myPojos";
            } else if (firstElement instanceof MyWin) {
                elementsFieldName = "myWins";
            } else {
                throw new IllegalArumentException("Unknown type");
            }
            serializers.defaultSerializeField(elementsFieldName, value.getElements(), gen);
        }
        gen.writeEndObject();
    }
}

Now you just need to tell Jackson to use this serializer instead of the default one.

@JsonSerialize(using = PagedResultSerializer.class)
public class PagedResult<T> {
    // Your code
}

Improvments: Add a Class<T> elementsType attribute to your PagedResult and use this attribute in your serializer instead of checking the first element in the list.

magicmn
  • 1,787
  • 7
  • 15
  • Thank you for this. I think the correct answer is "No I cant rename a variable in generic class". But your answer helped me to get around my problem. – Phiilschke Feb 24 '21 at 07:27
1

Another approach: use inheritance.

Have an abstract base class PagedResult that contains all the common fields, to then distinctively subclass it to PagedResultWithElements, PagedResultWithMyPojo and so on. And the subclasses contain just that "type" specific list.

As a drawback: you get some code duplication. But on the other side, you get quite more control over what happens without doing overly complicated (de)serialization based on custom code.

So, when you know the different flavors of "element types", and we talk say 3, max 5 different classes, using inheritance might be a viable solution.

GhostCat
  • 137,827
  • 25
  • 176
  • 248