Comment retourner une réponse JSON partielle en utilisant Java?

je construis une API RESTful et je veux fournir aux développeurs l'option de choisir les champs à retourner dans la réponse JSON. ce billet de blog montre des exemples de la façon dont plusieurs API (Google, Facebook, LinkedIn) permettent aux développeurs de personnaliser la réponse. Cela est considéré comme une réponse partielle.

Un exemple pourrait ressembler à ceci:

/users/123?fields=userId,fullname,title

dans l'exemple ci-dessus, L'API doit retourner les champs userId, fullName et title pour L'utilisateur "123".

je suis à la recherche d'idées sur la façon de mettre en œuvre ceci dans mon service Web reposant. J'utilise actuellement CXF (edit: et Jackson) mais je suis prêt à essayer une autre implémentation JAX-RS.

Voici ce que j'ai actuellement. Il renvoie un objet utilisateur complet. Comment puis-je retourner Seulement les champs que l'appelant de L'API veut à l'exécution basée sur le paramétrage des "champs"? Je ne veux pas rendre les autres champs Nuls. Je ne veux tout simplement pas de retour ils.

@GET
@Path("/{userId}")
@Produces("application/json")
public User getUser(@PathParam("userId") Long userId, 
    @DefaultValue("userId,fullname,title") @QueryParam("fields") String fields) {

User user = userService.findOne(userId);

StringTokenizer st = new StringTokenizer(fields, ",");
while (st.hasMoreTokens()) {

    // here's where i would like to select only the fields i want to return

}
return user;
}

mise à jour:

j'ai suivi unludo du lien qui alors lié à ceci: http://wiki.fasterxml.com/JacksonFeatureJsonFilter

Avec cette info, j'ai ajouté @JsonFilter("myFilter") à ma classe de domaine. Puis j'ai modifié ma méthode RESTful service pour retourner String au lieu de User comme suit:

@GET
@Path("/{userId}")
@Produces("application/json")
public String getUser(@PathParam("userId") Long userId,
                    @DefaultValue("userId,fullname,title") @QueryParam("fields") String fields) {

    User user = userService.findOne(userId);

    StringTokenizer st = new StringTokenizer(fields, ",");
    Set<String> filterProperties = new HashSet<String>();
    while (st.hasMoreTokens()) {
        filterProperties.add(st.nextToken());
    }

    ObjectMapper mapper = new ObjectMapper();
    FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",
                SimpleBeanPropertyFilter.filterOutAllExcept(filterProperties));

    try {
        String json = mapper.filteredWriter(filters).writeValueAsString(user);
        return json;
    } catch (IOException e) {
        e.printStackTrace();
    return e.getMessage();
    }
}

j'ai besoin de faire plus de test, mais c'est très bien.

40
demandé sur Justin 2012-02-16 20:10:15

3 réponses

si vous utilisez Jackson( un grand JSON lib-sorte de la norme pour Java je crois), vous pouvez utiliser le @View annotation de filtrer ce que vous voulez dans l'objet résultant.

je comprends que vous voulez quelque chose de dynamique, donc c'est un peu plus compliqué. Vous trouverez ce que vous cherchez ici: http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html (regardez 6. Filtrage entièrement dynamique:@JsonFilter).

17
répondu unludo 2016-06-30 23:22:21

créer une instance ObjectMapper à l'intérieur de la méthode resource pour chaque requête peut avoir des performances significatives. Selon le Jackson performances les meilleures pratiques les cartographes d'objets coûtent cher à créer.

à la place, vous pouvez personnaliser le rédacteur D'objets Jackson du fournisseur JAX-RS à l'intérieur de la méthode ressource en utilisant The Jackson 2.3 ObjectWriterModifier / ObjectReaderModifier feature.

Voici un exemple montre comment enregistrez un objet Local ObjectWriterModifier thread qui modifie l'ensemble des filtres appliqués au fournisseur JAX-RS Jackson utilisé dans une méthode ressource. Notez que je n'ai pas testé le code par rapport à une implémentation JAX-RS.

public class JacksonObjectWriterModifier2 {

    private static class FilterModifier extends ObjectWriterModifier {
        private final FilterProvider provider;

        private FilterModifier(FilterProvider provider) {
            this.provider = provider;
        }

        @Override
        public ObjectWriter modify(EndpointConfigBase<?> endpoint, MultivaluedMap<String, Object> responseHeaders,
                                   Object valueToWrite, ObjectWriter w, JsonGenerator g) throws IOException {
            return w.with(provider);
        }
    }

    @JsonFilter("filter1")
    public static class Bean {
        public final String field1;
        public final String field2;

        public Bean(String field1, String field2) {
            this.field1 = field1;
            this.field2 = field2;
        }
    }

    public static void main(String[] args) throws IOException {
        Bean b = new Bean("a", "b");
        JacksonJsonProvider provider = new JacksonJsonProvider();
        ObjectWriterInjector.set(new FilterModifier(new SimpleFilterProvider().addFilter("filter1",
                SimpleBeanPropertyFilter.filterOutAllExcept("field1"))));

        provider.writeTo(b, Bean.class, null, null, MediaType.APPLICATION_JSON_TYPE, null, System.out);
    }

}

Sortie:

{"field1":"a"}
5
répondu Alexey Gavrilov 2014-05-22 20:22:43

La Bibliothèque de jersey, de l'entité de filtrage Peut faire :

https://github.com/jersey/jersey/tree/2.22.2/examples/entity-filtering-selectable

https://jersey.java.net/documentation/latest/entity-filtering.html

Exemple:

Mon Objet

public class Address {

    private String streetAddress;
    private String region;
    private PhoneNumber phoneNumber;
}

URL

/1234?sélectionnez=streetAddress,région

RETURN

{
   "streetAddress": "2 square Tyson",
   "region": "Texas"
}

Ajouter à la Maven

<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-entity-filtering</artifactId>
    <version>2.22.2</version>
</dependency>
3
répondu Hydral 2016-03-18 16:59:29