Frage Wie gibt man eine partielle JSON-Antwort mit Java zurück?


Ich baue eine RESTful-API und möchte Entwicklern die Möglichkeit geben, auszuwählen, welche Felder in der JSON-Antwort zurückgegeben werden sollen. Dieser Blogbeitrag zeigt Beispiele, wie verschiedene APIs (Google, Facebook, LinkedIn) es Entwicklern ermöglichen, die Antwort anzupassen. Dies wird als partielle Antwort bezeichnet.

Ein Beispiel könnte so aussehen:

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

Im obigen Beispiel sollte die API die Felder userId, fullName und title für den Benutzer "123" zurückgeben.

Ich bin auf der Suche nach Ideen, wie ich dies in meinem RESTful Web Service implementieren kann. Ich benutze derzeit CXF (edit: und Jackson), bin aber bereit, eine andere JAX-RS-Implementierung zu versuchen.

Hier ist, was ich derzeit habe. Es gibt ein vollständiges Benutzerobjekt zurück. Wie kann ich nur die Felder zurückgeben, die der API-Aufrufer zur Laufzeit auf der Grundlage des Paramaeter "Felder" benötigt? Ich möchte die anderen Felder Null nicht machen. Ich will sie einfach nicht zurückgeben.

@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;
}

AKTUALISIEREN:

Ich folgte dem Link von unludo, der damit verbunden war: http://wiki.fasterxml.com/JacksonFeatureJsonFilter

Mit dieser Information fügte ich hinzu @JsonFilter("myFilter") zu meiner Domain-Klasse. Dann änderte ich meine RESTful-Service-Methode, um String statt User wie folgt zurückzugeben:

@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();
    }
}

Ich muss mehr testen, aber so weit, so gut.


39
2018-02-16 16:10


Ursprung


Antworten:


Wenn Sie Jackson (eine großartige JSON lib - Art des Standards für Java, glaube ich) verwenden, können Sie die @View Annotation, um zu filtern, was Sie im resultierenden Objekt wollen.

Ich verstehe, dass Sie etwas Dynamisches wollen, also ist es ein bisschen komplizierter. Hier finden Sie, was Sie suchen: http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html (siehe 6. Volldynamische Filterung: @JsonFilter).

Ich würde mich für die Lösung interessieren, die Sie finden werden.


17
2018-02-16 16:55



Das Erstellen einer ObjectMapper-Instanz in der Ressourcenmethode für jede Anforderung kann zu erheblichen Leistungseinbußen führen. Gemäß die Best Practices von Jackson Objektmapper sind teuer zu erstellen.

Stattdessen können Sie den Jackson-Objektschreiber des JAX-RS-Anbieters innerhalb der Ressourcenmethode anpassen die Jackson 2.3 ObjectWriterModifier / ObjectReaderModifier-Funktion.

Im folgenden Beispiel wird gezeigt, wie ein lokales ObjectWriterModifier-Thread-Objekt registriert wird, das den Filtersatz für den JAX-RS Jackson-Provider ändert, der in einer Ressourcenmethode verwendet wird. Beachten Sie, dass ich den Code nicht mit einer JAX-RS-Implementierung getestet habe.

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);
    }

}

Ausgabe:

{"field1":"a"}

5
2018-05-22 20:22



Die Bibliothek Trikot-Entity-Filterung kann Folgendes tun:

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

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

Beispiel:

Mein Objekt

public class Address {

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

URL

Personen / 1234? select = streetAddress, Region

RÜCKKEHR

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

Zu Maven hinzufügen

<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-entity-filtering</artifactId>
    <version>2.22.2</version>
</dependency>

3
2018-03-18 16:59