Skip to content

Instantly share code, notes, and snippets.

@billybong
Last active December 12, 2025 01:39
Show Gist options
  • Select an option

  • Save billybong/0b2963c85912f4a2ee7b591dd85a93b6 to your computer and use it in GitHub Desktop.

Select an option

Save billybong/0b2963c85912f4a2ee7b591dd85a93b6 to your computer and use it in GitHub Desktop.
JacksonBodyHandler for parsing JSON responses directly from Java 11+ http clients
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.http.HttpResponse;
import java.util.function.Function;
public class JacksonBodyHandlers {
private final ObjectMapper objectMapper;
public JacksonBodyHandlers(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public HttpResponse.BodyHandler<JsonNode> jsonNodeHandler(){
return responseInfo -> subscriberFrom(objectMapper::readTree);
}
public <T> HttpResponse.BodyHandler<T> handlerFor(TypeReference<T> typeReference){
return responseInfo -> subscriberFrom((bytes) -> objectMapper.readValue(bytes, typeReference));
}
public <T> HttpResponse.BodyHandler<T> handlerFor(Class<T> clazz){
return responseInfo -> subscriberFrom((bytes) -> objectMapper.readValue(bytes, clazz));
}
private <T> HttpResponse.BodySubscriber<T> subscriberFrom(IOFunction<byte[], T> ioFunction) {
return HttpResponse.BodySubscribers.mapping(HttpResponse.BodySubscribers.ofByteArray(), rethrowRuntime(ioFunction));
}
@FunctionalInterface
interface IOFunction<V, T> {
T apply(V value) throws IOException;
}
private static <V,T> Function<V,T> rethrowRuntime(IOFunction<V, T> ioFunction){
return (V v) -> {
try{
return ioFunction.apply(v);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
}
}
@billybong
Copy link
Author

Simple utility class for leveraging Jackson to parse Http responses from the standard Java 11+ http client.
Oh god how I wish the JDK came with native JSON support...

@bademux
Copy link

bademux commented Nov 11, 2020

@nithsua
Copy link

nithsua commented Feb 1, 2025

@billybong doing gods work thank you mate

@onacit
Copy link

onacit commented Dec 12, 2025

According to the link by @bademux, the following utility class, which defer the result using Supplier, may help.

// https://github.com/jinahya/rickandmortyapi-client/blob/e24ec128d1f5398da8cc0c28fd8c392365035903/rickandmortyapi-client-jre/src/main/java/io/github/jinahya/rickandmortyapi/client/jre/_JavaNetHttpUtils.java

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.http.HttpResponse;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;

@SuppressWarnings({
        "java:S101" // Class names should comply with a naming convention
})
final class _JavaNetHttpUtils {

    static <T, U> HttpResponse.BodyHandler<Supplier<U>> newJsonBodyHandler(
            final HttpResponse.BodySubscriber<T> upstream,
            final Function<? super T, ? extends U> deserializer) {
        Objects.requireNonNull(upstream, "upstream is null");
        Objects.requireNonNull(deserializer, "deserializer is null");
        return ri -> {
            final var statusCode = ri.statusCode();
            if (statusCode == HttpURLConnection.HTTP_NOT_FOUND) {
                return HttpResponse.BodySubscribers.mapping(
                        HttpResponse.BodySubscribers.discarding(),
                        b -> () -> null
                );
            }
            return HttpResponse.BodySubscribers.mapping(
                    upstream,
                    b -> () -> {
                        return deserializer.apply(b);
                    }
            );
        };
    }

    static <T> HttpResponse.BodyHandler<Supplier<T>> newJsonBodyHandler(final ObjectMapper objectMapper,
                                                                        final Class<T> valueType) {
        Objects.requireNonNull(objectMapper, "objectMapper is null");
        Objects.requireNonNull(valueType, "valueType is null");
        return newJsonBodyHandler(
                HttpResponse.BodySubscribers.ofInputStream(),
                b -> {
                    try {
                        return objectMapper.readValue(b, valueType);
                    } catch (final IOException ioe) {
                        throw new RuntimeException(ioe);
                    }
                }
        );
    }

    static <T> HttpResponse.BodyHandler<Supplier<T>> newJsonBodyHandler(final ObjectMapper objectMapper,
                                                                        final TypeReference<T> typeReference) {
        Objects.requireNonNull(objectMapper, "objectMapper is null");
        Objects.requireNonNull(typeReference, "typeReference is null");
        return newJsonBodyHandler(
                HttpResponse.BodySubscribers.ofInputStream(),
                b -> {
                    try {
                        return objectMapper.readValue(b, typeReference);
                    } catch (final IOException ioe) {
                        throw new RuntimeException(ioe);
                    }
                }
        );
    }

    // -----------------------------------------------------------------------------------------------------------------
    private _JavaNetHttpUtils() {
        throw new AssertionError("instantiation is not allowed");
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment