Build a Flexible Java Open Platform SDK: HTTP, Serialization, API Client

This article walks through the design and implementation of a Java open‑platform SDK, covering a lightweight HTTP client, flexible serialization adapters for JSON and XML, and a simple API client, while sharing practical code examples and best‑practice tips for building maintainable backend integrations.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Build a Flexible Java Open Platform SDK: HTTP, Serialization, API Client

Hello everyone, I'm Bage! After the previous article on the design ideas of the Taobao Open Platform API, many readers asked for the next piece, so this article presents the design ideas for an open platform SDK and demonstrates step‑by‑step implementation.

Data Transmission Module: handles request data transmission using the HTTP protocol.

Serialization Module: provides serialization and deserialization, supporting formats such as JSON and XML.

Application Module: coordinates data transmission and serialization, and performs request signing.

Data Transmission

The transmission protocol uses the widely supported http protocol. Although a generic design could support other protocols like socket, the added complexity is unnecessary for most cases.

/**
 * http protocol interface
 */
public interface HttpClient {

    /**
     * Execute an HTTP POST request
     * @param url
     * @param data
     * @param headers
     * @return response body
     */
    String post(String url, byte[] data, Map<String, List<String>> headers) throws HttpClientException;
}

A simple implementation using HttpURLConnection follows:

package com.javaobj.sdk.http.impl;

public class DefaultHttpClient implements HttpClient {

    public String post(String url, byte[] data, Map<String, List<String>> headers) throws HttpClientException {
        URL targetUrl = null;
        try {
            targetUrl = new URL(url);
        } catch (MalformedURLException e) {
            throw new HttpClientException("Invalid URL");
        }
        try {
            HttpURLConnection connection = (HttpURLConnection) targetUrl.openConnection();
            connection.setDoInput(true);
            connection.setDoOutput(true);
            connection.setRequestMethod("POST");
            if (headers != null) {
                for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
                    for (String value : entry.getValue()) {
                        connection.setRequestProperty(entry.getKey(), value);
                    }
                }
            }
            connection.connect();
            OutputStream out = connection.getOutputStream();
            out.write(data);
            out.close();
            InputStream in = connection.getInputStream();
            ByteOutputStream body = new ByteOutputStream();
            int ch;
            while ((ch = in.read()) != -1) {
                body.write(ch);
            }
            in.close();
            String responseBody = new String(body.getBytes());
            int statusCode = connection.getResponseCode();
            boolean is2xxSuccessful = statusCode >= 200 && statusCode <= 299;
            boolean is3xxSuccessful = statusCode >= 300 && statusCode <= 399;
            if (!(is2xxSuccessful || is3xxSuccessful)) {
                throw new HttpStatusClientException(statusCode, connection.getResponseMessage(), responseBody);
            }
            return responseBody;
        } catch (IOException e) {
            throw new HttpClientException(e);
        }
    }
}

Serialization and Deserialization

Serialization converts Java objects to byte streams, and deserialization does the reverse. Two simple interfaces are defined:

Serializer

Deserializer

JacksonSerializer

public class JacksonSerializer implements Serializer {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public JacksonSerializer() {
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
    }

    public byte[] serialize(Object obj) {
        try {
            return objectMapper.writeValueAsBytes(obj);
        } catch (JsonProcessingException e) {
            throw new IllegalStateException(e);
        }
    }
}

JacksonDeserializer

public class JacksonDeserializer implements Deserializer {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public JacksonDeserializer() {
        objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
    }

    public <T> T deserialize(byte[] data, Class<T> clz) {
        try {
            return objectMapper.readValue(data, clz);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }
}

To support multiple formats, a SerializationAdapter interface extends both Serializer and Deserializer and adds a support(String format) method.

public interface SerializationAdapter extends Serializer, Deserializer {

    /**
     * Determine whether the adapter supports the given format
     */
    boolean support(String format);
}

JSONSerializationAdapter

public class JSONSerializationAdapter implements SerializationAdapter {

    private final Serializer serializer;
    private final Deserializer deserializer;

    public JSONSerializationAdapter(Serializer serializer, Deserializer deserializer) {
        this.serializer = serializer;
        this.deserializer = deserializer;
    }

    public JSONSerializationAdapter() {
        this(new JacksonSerializer(), new JacksonDeserializer());
    }

    @Override
    public boolean support(String format) {
        return format != null && Objects.equals(format.toLowerCase(), "json");
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clz) {
        return deserializer.deserialize(data, clz);
    }

    @Override
    public byte[] serialize(Object obj) {
        return serializer.serialize(obj);
    }
}

JAXBSerializer

public class JAXBSerializer implements Serializer {
    @Override
    public byte[] serialize(Object obj) {
        ByteOutputStream outputStream = new ByteOutputStream();
        JAXB.marshal(obj, outputStream);
        return outputStream.getBytes();
    }
}

JAXBDeserializer

public class JAXBDeserializer implements Deserializer {
    @Override
    public <T> T deserialize(byte[] data, Class<T> clz) {
        return JAXB.unmarshal(new StringReader(new String(data).trim()), clz);
    }
}

XMLSerializationAdapter

public class XMLSerializationAdapter implements SerializationAdapter {

    private final Serializer serializer;
    private final Deserializer deserializer;

    public XMLSerializationAdapter(Serializer serializer, Deserializer deserializer) {
        this.serializer = serializer;
        this.deserializer = deserializer;
    }

    public XMLSerializationAdapter() {
        this(new JAXBSerializer(), new JAXBDeserializer());
    }

    @Override
    public boolean support(String format) {
        return format != null && Objects.equals(format.toLowerCase(), "xml");
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clz) {
        return deserializer.deserialize(data, clz);
    }

    @Override
    public byte[] serialize(Object obj) {
        return serializer.serialize(obj);
    }
}

How to Use?

public class SerializationTest {

    @Test
    public void testSerialization() {
        String format = "xml";
        List<SerializationAdapter> adapters = new ArrayList<>();
        adapters.add(new JSONSerializationAdapter());
        adapters.add(new XMLSerializationAdapter());

        Person person = new Person();
        person.setName("Bage");
        person.setAge(18);

        for (SerializationAdapter adapter : adapters) {
            if (adapter.support(format)) {
                byte[] data = adapter.serialize(person);
                System.out.println(format + " serialized data: " + new String(data));
                Person person2 = adapter.deserialize(data, Person.class);
                System.out.println(format + " deserialized data: " + person2);
            }
        }
    }
}

API Client

A simple SDK client interface abstracts request execution and response handling.

public interface SdkClient {

    /**
     * Execute an API request
     */
    <T extends AbstractResponse> T execute(AbstractRequest<T> req);
}

AbstractRequest

public abstract class AbstractRequest<T extends AbstractResponse> {

    /** Request method name */
    public abstract String getMethod();

    /** API parameters */
    public abstract Map<String, Object> getApiParams();

    /** Response class */
    public abstract Class<T> getResponseClass();
}

AbstractResponse

public class AbstractResponse {
    private String code;
    private String msg;

    public boolean isSuccess() {
        return Objects.equals(code, "0");
    }
}

DefaultClient

public class DefaultClient implements SdkClient {

    private SerializationAdapter serializationAdapter = new JSONSerializationAdapter();
    private HttpClient httpClient = new DefaultHttpClient();
    private final String endpoint;
    private final String appid;
    private final String appsecret;

    public DefaultClient(String endpoint, String appid, String appsecret) {
        this.endpoint = endpoint;
        this.appid = appid;
        this.appsecret = appsecret;
    }

    @Override
    public <T extends AbstractResponse> T execute(AbstractRequest<T> req) {
        Map<String, Object> params = req.getApiParams();
        String timestamp = System.currentTimeMillis() + "";
        StringBuilder paramsStr = new StringBuilder();
        Map<String, Object> treeMap = new TreeMap<>(params);
        for (Map.Entry<String, Object> entry : treeMap.entrySet()) {
            paramsStr.append(entry.getKey()).append(entry.getValue());
        }
        String sign = Md5Util.md5(appsecret + appid + req.getMethod() + req.getVersion() + paramsStr.toString() + appsecret);
        String url = endpoint + "?method=" + req.getMethod() + "&version=" + req.getVersion() + "×tamp=" + timestamp + "&sign=" + sign + "&appid=" + appid;
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Content-type", Collections.singletonList("application/json"));
        byte[] body = serializationAdapter.serialize(req.getApiParams());
        String response = httpClient.post(url, body, headers);
        return serializationAdapter.deserialize(response.getBytes(), req.getResponseClass());
    }
}

Summary

Prefer low‑version JDK (1.5/1.6) to maximize runtime compatibility.

Minimize third‑party dependencies to avoid version conflicts.

Use Java POJOs for request and response parameters to ensure type safety for SDK users.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaSDKserializationHTTPAPI
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.