×

用Java语言解析京东商品评论接口的JSON数据时,如何处理可能出现的异常情况?

知名用户18007905473 知名用户18007905473 发表于2025-12-01 16:35:06 浏览40 评论0

抢沙发发表评论

Java 解析京东商品评论 API JSON 数据:异常处理完整方案

在 Java 调用京东商品评论 API 并解析 JSON 数据时,异常可能出现在 HTTP 请求阶段(如网络波动、权限失效)、JSON 解析阶段(如字段缺失、格式异常)、数据使用阶段(如空指针)。以下是贴合实际开发的异常处理方案,基于「京东开放平台合法授权 API」,兼顾实用性与健壮性。

一、核心异常类型与处理原则

1. 需重点处理的 5 类异常

异常场景对应 Java 异常 / 错误码影响范围
网络问题(超时、断网)ConnectTimeoutExceptionSocketTimeoutException请求直接失败
API 授权 / 参数错误接口返回非 200 码(如 401 权限失效、400 参数错误)无有效 JSON 响应
JSON 格式异常JsonParseException(响应非标准 JSON)解析失败
JSON 字段缺失 / 类型不匹配NullPointerExceptionJsonMappingException部分数据提取失败
数据为空(无评论)接口返回 200,但 comments 为空数组业务逻辑无数据可用

2. 处理原则

  • 「分层捕获」:HTTP 请求、JSON 解析、数据使用分开捕获异常,不混用;

  • 「优雅降级」:某条评论解析失败不影响整体流程,无评论时给出明确提示;

  • 「日志留痕」:所有异常记录关键信息(如请求参数、错误栈),便于排查;

  • 「合规兜底」:解析失败时不泄露敏感信息,不强制使用异常数据。

二、完整实现代码(含异常处理)

以「HttpClient + Jackson」组合为例(企业级开发常用),包含全链路异常处理:

1. 依赖准备(Maven)

xml
<!-- HTTP 请求库 --><dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version></dependency><!-- JSON 解析库 --><dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version></dependency><!-- 日志库(记录异常) --><dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.36</version></dependency><dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.11</version></dependency>

2. 核心代码(含异常处理)

java
运行
import org.apache.http.HttpEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.utils.URIBuilder;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.databind.ObjectMapper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.net.URISyntaxException;import java.util.ArrayList;import java.util.List;// 评论数据模型(用于结构化存储解析结果)class JdComment {
    private String commentId;   // 评论ID
    private String userNickname;// 脱敏昵称
    private int score;          // 评分
    private String content;     // 评论内容
    private String productAttr; // 购买规格
    private String commentTime; // 评论时间

    // getter/setter 省略(按需生成)}public class JdCommentParserWithException {
    // 日志记录(关键异常信息)
    private static final Logger logger = LoggerFactory.getLogger(JdCommentParserWithException.class);
    // JSON 解析器(全局单例,避免重复创建)
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    // 超时配置(避免无限等待)
    private static final int CONNECT_TIMEOUT = 5000;  // 连接超时:5秒
    private static final int SOCKET_TIMEOUT = 10000;  // 响应超时:10秒

    public static void main(String[] args) {
        // 合法授权参数(替换为你的真实信息)
        String appKey = "你的AppKey";
        String accessToken = "你的AccessToken";
        String productId = "目标商品ID";

        // 调用核心方法,捕获顶层异常
        try {
            List<JdComment> commentList = parseJdComments(appKey, accessToken, productId);
            logger.info("解析完成,共获取 {} 条有效评论", commentList.size());
            // 后续业务处理(如存储、统计)
        } catch (Exception e) {
            // 顶层异常兜底,不影响程序退出
            logger.error("京东评论解析全流程失败:{}", e.getMessage(), e);
            System.out.println("评论解析失败,请稍后重试");
        }
    }

    /**
     * 核心方法:调用 API + 解析 JSON + 异常处理
     */
    public static List<JdComment> parseJdComments(String appKey, String accessToken, String productId) throws Exception {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;

        try {
            // 1. 构建 HTTP 客户端(配置超时)
            httpClient = HttpClients.custom()
                    .setConnectionTimeToLive(CONNECT_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS)
                    .setDefaultSocketConfig(org.apache.http.config.SocketConfig.custom()
                            .setSoTimeout(SOCKET_TIMEOUT)
                            .build())
                    .build();

            // 2. 构建请求 URL(避免参数拼接错误)
            URIBuilder uriBuilder = new URIBuilder("https://api.jd.com/routerjson");
            uriBuilder.addParameter("app_key", appKey)
                    .addParameter("access_token", accessToken)
                    .addParameter("method", "jingdong.comment.list.get")
                    .addParameter("productId", productId)
                    .addParameter("page", "1")
                    .addParameter("pageSize", "20");

            HttpGet httpGet = new HttpGet(uriBuilder.build());
            logger.info("发送评论 API 请求:{}", httpGet.getURI());

            // 3. 发送 HTTP 请求(捕获网络相关异常)
            response = httpClient.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();

            // 4. 处理 API 响应状态码(非 200 均视为异常)
            if (statusCode != 200) {
                String errorMsg = String.format("API 调用失败,状态码:%d,响应:%s",
                        statusCode, EntityUtils.toString(response.getEntity(), "UTF-8"));
                logger.error(errorMsg);
                throw new RuntimeException(errorMsg); // 抛出业务异常,上层处理
            }

            // 5. 读取响应数据
            HttpEntity entity = response.getEntity();
            String jsonStr = EntityUtils.toString(entity, "UTF-8");
            EntityUtils.consume(entity); // 释放资源

            // 6. 解析 JSON 数据(单独捕获 JSON 相关异常)
            return parseJsonToComments(jsonStr);

        } catch (URISyntaxException e) {
            // URL 构建异常(参数非法,如含特殊字符)
            String errorMsg = "请求 URL 构建失败,检查参数是否合法";
            logger.error(errorMsg, e);
            throw new Exception(errorMsg, e);
        } catch (org.apache.http.conn.ConnectTimeoutException e) {
            // 连接超时(网络问题或 API 服务不可用)
            String errorMsg = "API 连接超时,请检查网络或稍后重试";
            logger.error(errorMsg, e);
            throw new Exception(errorMsg, e);
        } catch (org.apache.http.conn.HttpHostConnectException e) {
            // 无法连接主机(API 地址错误或服务下线)
            String errorMsg = "无法连接京东 API 服务器,请检查地址是否正确";
            logger.error(errorMsg, e);
            throw new Exception(errorMsg, e);
        } catch (JsonProcessingException e) {
            // JSON 解析异常(响应格式非法)
            String errorMsg = String.format("JSON 解析失败,原始响应:%s", jsonStr);
            logger.error(errorMsg, e);
            throw new Exception(errorMsg, e);
        } catch (Exception e) {
            // 其他未捕获异常
            String errorMsg = "HTTP 请求阶段异常";
            logger.error(errorMsg, e);
            throw new Exception(errorMsg, e);
        } finally {
            // 7. 释放资源(必须在 finally 中执行,避免资源泄露)
            if (response != null) {
                try {
                    response.close();
                } catch (Exception e) {
                    logger.warn("关闭响应流失败", e);
                }
            }
            if (httpClient != null) {
                try {
                    httpClient.close();
                } catch (Exception e) {
                    logger.warn("关闭 HTTP 客户端失败", e);
                }
            }
        }
    }

    /**
     * 解析 JSON 字符串为评论列表(处理字段缺失、类型不匹配)
     */
    private static List<JdComment> parseJsonToComments(String jsonStr) throws JsonProcessingException {
        List<JdComment> commentList = new ArrayList<>();
        JsonNode rootNode = OBJECT_MAPPER.readTree(jsonStr);

        // 校验 JSON 根节点核心字段(避免空指针)
        if (!rootNode.has("code") || !rootNode.has("data")) {
            logger.error("JSON 响应缺少核心字段,原始数据:{}", jsonStr);
            return commentList; // 优雅降级,返回空列表
        }

        // 检查 API 业务状态码(部分接口 200 但 code 非 0/200)
        int businessCode = rootNode.get("code").asInt(-1);
        if (businessCode != 200) {
            String businessMsg = rootNode.has("message") ? rootNode.get("message").asText("未知错误") : "未知错误";
            logger.error("API 业务失败,错误码:{},错误信息:{}", businessCode, businessMsg);
            return commentList;
        }

        // 提取评论列表(字段可能不存在,需判断)
        JsonNode dataNode = rootNode.get("data");
        if (!dataNode.has("comments")) {
            logger.warn("JSON 响应中无评论数据");
            return commentList;
        }

        JsonNode commentsNode = dataNode.get("comments");
        if (!commentsNode.isArray()) {
            logger.error("comments 字段不是数组,原始数据:{}", jsonStr);
            return commentList;
        }

        // 遍历每条评论,处理字段缺失/类型不匹配
        for (JsonNode commentNode : commentsNode) {
            JdComment comment = new JdComment();
            try {
                // 字段提取:使用默认值避免空指针,类型转换失败时捕获异常
                comment.setCommentId(commentNode.has("commentId") ? commentNode.get("commentId").asText("") : "");
                comment.setUserNickname(commentNode.has("userNickname") ? commentNode.get("userNickname").asText("匿名用户") : "匿名用户");
                // 评分:默认 0 分(字段不存在或非数字时)
                comment.setScore(commentNode.has("score") && commentNode.get("score").isInt() ? commentNode.get("score").asInt() : 0);
                comment.setContent(commentNode.has("content") ? commentNode.get("content").asText("") : "");
                comment.setProductAttr(commentNode.has("productAttr") ? commentNode.get("productAttr").asText("未知规格") : "未知规格");
                comment.setCommentTime(commentNode.has("commentTime") ? commentNode.get("commentTime").asText("") : "");

                commentList.add(comment);
            } catch (Exception e) {
                // 单条评论解析失败,不影响整体,仅记录日志
                logger.warn("单条评论解析失败,原始数据:{},错误:{}", commentNode.toString(), e.getMessage(), e);
            }
        }

        return commentList;
    }}

三、关键异常处理细节说明

1. 网络相关异常:避免无限等待

  • 配置「连接超时」和「响应超时」,防止程序因网络问题阻塞;

  • 单独捕获 ConnectTimeoutException(连接超时)、HttpHostConnectException(无法连接),给出明确的用户提示。

2. API 响应状态码:非 200 均处理

  • 即使 HTTP 状态码是 200,仍需检查 JSON 中的 code 字段(部分接口业务失败时 HTTP 状态码仍为 200);

  • 记录完整的错误响应(如 401 时的权限提示),便于排查问题(如 Access Token 过期)。

3. JSON 解析:字段容错 + 单条失败不影响整体

  • 所有字段提取前先判断「是否存在」+「类型匹配」(如 score 必须是数字),避免 NullPointerException

  • 单条评论解析失败时,仅记录日志并跳过,不中断整个列表的解析(如某条评论缺失 productAttr 字段,不影响其他评论)。

4. 资源释放:finally 中关闭流

  • HTTP 客户端、响应流必须在 finally 中关闭,避免资源泄露;

  • 关闭资源时单独捕获异常,不影响其他逻辑执行。

5. 日志记录:便于排查

  • 记录关键信息:请求 URL、状态码、原始 JSON 响应、异常栈;

  • 区分日志级别:错误(如 API 授权失败)、警告(如单条评论解析失败)、信息(如请求成功)。

四、额外优化建议

  1. 重试机制:对网络超时、503(服务暂时不可用)等异常,可添加重试逻辑(使用 spring-retry 等框架),避免偶发问题;

  2. 参数校验:调用 parseJdComments 前,校验 appKeyproductId 等参数非空,减少无效请求;

  3. 限流控制:按京东 API 约定的 QPS 调用,避免因频率超限导致 429 异常;

  4. 数据脱敏:若需打印评论内容,对用户昵称、评论中的敏感信息(如手机号)进行脱敏,符合隐私合规要求。

总结

Java 解析京东商品评论 API 的异常处理,核心是「分层捕获、优雅降级、日志留痕」:
  • 网络层:处理超时、连接失败,配置合理超时时间;

  • API 层:校验 HTTP 状态码和业务状态码,记录错误响应;

  • JSON 层:字段容错,单条解析失败不影响整体;

  • 资源层:确保流关闭,避免泄露。

该方案可覆盖 99% 以上的实际异常场景,同时保证代码健壮性和可维护性,适合企业级开发使用。


群贤毕至

访客