HashMap实现通用的Request和Response及解析非标准JSON
通过HashMap实现通用response和request、自建map转JSON工具类
一、前言
还是一样的,不想听笔者叨叨的点击右侧目录跳转到代码部分,自取完对你有帮助的点个免费的收藏和赞吧,咱就是说!因为畅捷通的开放平台的某个API他的输入参数在文档中是固定的,但是他的输出参数直接就是个Object,完事里面输出出来还是一堆数据,笔者原来也没有尝试自己搭建自己的通用类型的response和request,头大啊,提交总部希望总部开发支持,最后想了想总部上次也没说什么,直接回了个”无脚本“,还是自己弄出来的,干脆自己弄吧!然后就给捣鼓了半天捣鼓出来了;
其实其中最难弄的就是解析JSON工具类,字符分割真麻烦,最后还好给弄出来了,废话少叙,一起看看代码吧!
二、先实现具体的一个业务接口
首先其实我是通过新建了个通用报表的一个类型,由简入难,先写出一个具体的再去给他改成通用的,因为访问的畅捷通开放平台的API接口其实是一样的,只是可以通过RequestBody的调整去获取不同报表的参数;
这个的代码如下:
/**
* 获取收发存汇总表
*
* @param username
* @param password
* @param map
* @return
* @throws ChanjetApiException
*/
@ApiOperation(value = "获取收发存汇总表", httpMethod = "POST")
@PostMapping("/report/SaleDeliveryDetail")
public SaleDeliveryDetailReportResponse getSaleDeliveryDetailReport(@RequestHeader("username") String username, @RequestHeader("password") String password, @RequestBody LinkedHashMap<String, Object> map) throws ChanjetApiException {
Object request = map.get("request");
SaleDeliveryDetailReportRequest saleDeliveryDetailReportRequest = new SaleDeliveryDetailReportRequest();
saleDeliveryDetailReportRequest.setAppKey(appKey);
saleDeliveryDetailReportRequest.setAppSecret(appSecret);
saleDeliveryDetailReportRequest.setOpenToken(accessToken);
saleDeliveryDetailReportRequest.setRequestUri("/tplus/api/v2/reportQuery/GetReportData");
saleDeliveryDetailReportRequest.setBizContent(new SaleDeliveryDetailReportContent(RequestParserUtils.parseRequest(request)));
return chanjetClient.execute(saleDeliveryDetailReportRequest);
}
主要还是要关注转换类,要将他转化类转换为对应的畅捷通对应API的入参标准的Object:
package com.chanjet.magic.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.chanjet.magic.dto.ReportGroupItem;
import com.chanjet.magic.dto.ReportOpenApiRequest;
import com.chanjet.magic.dto.ReportSearchItem;
import java.util.ArrayList;
import java.util.List;
/**
* ClassName: RequestParserUtils
* Package: com.chanjet.magic.utils
* Description: 将json字符串转换为标准JSON字符串,将取到的map格式的json数据串塞入对应的实体类中,这样再用实体类去塞入对应的Content中去;
*
* @Author Zeiyalo
* @Create 2025/4/10 12:19
* @Version 1.0.0
*/
public class RequestParserUtils {
public static ReportOpenApiRequest parseRequest(Object request) {
try {
String json = convertToJson(request.toString());
System.out.println("JSON 内容: " + json);
JSONObject requestJson = JSON.parseObject(json);
ReportOpenApiRequest data = new ReportOpenApiRequest();
data.setReportName(requestJson.getString("ReportName"));
data.setPageIndex(requestJson.getString("PageIndex"));
data.setPageSize(requestJson.getString("PageSize"));
data.setReportTableColNames(requestJson.getString("ReportTableColNames"));
// 处理 SearchItems
JSONArray searchItemsArray = requestJson.getJSONArray("SearchItems");
if (searchItemsArray != null) {
for (int i = 0; i < searchItemsArray.size(); i++) {
JSONObject searchItemJson = searchItemsArray.getJSONObject(i);
ReportSearchItem searchItem = new ReportSearchItem();
searchItem.setColumnName(searchItemJson.getString("ColumnName"));
searchItem.setBeginDefault(searchItemJson.getString("BeginDefault"));
searchItem.setBeginDefaultText(searchItemJson.getString("BeginDefaultText"));
searchItem.setEndDefault(searchItemJson.getString("EndDefault"));
searchItem.setEndDefaultText(searchItemJson.getString("EndDefaultText"));
data.getSearchItems().add(searchItem);
}
}
// 处理 GroupItems
JSONArray groupItemsArray = requestJson.getJSONArray("GroupItems");
if (groupItemsArray != null) {
for (int i = 0; i < groupItemsArray.size(); i++) {
JSONObject groupItemJson = groupItemsArray.getJSONObject(i);
ReportGroupItem groupItem = new ReportGroupItem();
groupItem.setColumnName(groupItemJson.getString("ColumnName"));
groupItem.setIsGroup(groupItemJson.getBoolean("isGroup"));
groupItem.setGrpFieldType(groupItemJson.getString("grpFieldType"));
groupItem.setDateFieldGrpStyle(groupItemJson.getString("dateFieldGrpStyle"));
groupItem.setCanHorizontalLayout(groupItemJson.getBoolean("canHorizontalLayout"));
groupItem.setIsHorizontalLayout(groupItemJson.getBoolean("isHorizontalLayout"));
data.getGroupItems().add(groupItem);
}
}
return data;
} catch (Exception e) {
System.err.println("解析错误: " + e.getMessage());
e.printStackTrace();
return null;
}
}
public static String convertToJson(String input) {
input = input.trim();
if (input.startsWith("{") && input.endsWith("}")) {
input = input.substring(1, input.length() - 1);
}
JSONObject resultJson = new JSONObject();
String[] keyValuePairs = splitKeyValuePairs(input);
for (String pair : keyValuePairs) {
int index = pair.indexOf("=");
if (index == -1) {
continue;
}
String key = pair.substring(0, index).trim();
String value = pair.substring(index + 1).trim();
if (value.startsWith("[") && value.endsWith("]")) {
JSONArray list = new JSONArray();
String[] items = splitListItems(value.substring(1, value.length() - 1));
for (String item : items) {
list.add(parseNestedObject(item));
}
resultJson.put(key, list);
} else {
resultJson.put(key, value);
}
}
return resultJson.toJSONString();
}
private static String[] splitKeyValuePairs(String input) {
List<String> pairs = new ArrayList<>();
int start = 0;
int depth = 0;
boolean inArray = false;
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (c == '[') {
inArray = true;
depth++;
} else if (c == ']') {
inArray = false;
depth--;
} else if (c == '{') {
depth++;
} else if (c == '}') {
depth--;
} else if (c == ',' && depth == 0 &&!inArray) {
// 特殊处理 ReportTableColNames,避免被逗号分割
if (input.substring(start, i).trim().startsWith("ReportTableColNames")) {
continue;
}
pairs.add(input.substring(start, i).trim());
start = i + 1;
}
}
pairs.add(input.substring(start).trim());
return pairs.toArray(new String[0]);
}
private static String[] splitListItems(String input) {
List<String> items = new ArrayList<>();
int start = 0;
int depth = 0;
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (c == '{') {
if (depth == 0) {
start = i;
}
depth++;
} else if (c == '}') {
depth--;
if (depth == 0) {
items.add(input.substring(start, i + 1));
}
}
}
return items.toArray(new String[0]);
}
private static JSONObject parseNestedObject(String input) {
JSONObject obj = new JSONObject();
String[] pairs = splitKeyValuePairs(input.substring(1, input.length() - 1));
for (String pair : pairs) {
int index = pair.indexOf("=");
if (index == -1) {
continue;
}
String key = pair.substring(0, index).trim();
String value = pair.substring(index + 1).trim();
obj.put(key, value);
}
return obj;
}
}
中间的主要方法parseRequest返回的类型就是一个实体类,复杂的也可以去遍历塞入数据:
下面是对应的参数实体类:
package com.chanjet.magic.dto;
import java.util.ArrayList;
import java.util.List;
/**
* ClassName: ReportOpenApiRequest
* Package: com.chanjet.magic.dto
* Description:
*
* @Author Zeiyalo
* @Create 2025/4/10 10:17
* @Version 1.0.0
*/
public class ReportOpenApiRequest {
private String ReportName;
private String planName;
private String PageIndex;
private String PageSize;
private List<ReportSearchItem> SearchItems = new ArrayList<>();
private String ReportTableColNames;
private List<ReportGroupItem> GroupItems = new ArrayList<>();
private String levelDisplay;
public String getReportName() {
return ReportName;
}
public void setReportName(String reportName) {
ReportName = reportName;
}
public String getLevelDisplay() {
return levelDisplay;
}
public void setLevelDisplay(String levelDisplay) {
this.levelDisplay = levelDisplay;
}
public List<ReportGroupItem> getGroupItems() {
return GroupItems;
}
public void setGroupItems(List<ReportGroupItem> groupItems) {
GroupItems = groupItems;
}
public String getReportTableColNames() {
return ReportTableColNames;
}
public void setReportTableColNames(String reportTableColNames) {
ReportTableColNames = reportTableColNames;
}
public List<ReportSearchItem> getSearchItems() {
return SearchItems;
}
public void setSearchItems(List<ReportSearchItem> searchItems) {
SearchItems = searchItems;
}
public String getPageSize() {
return PageSize;
}
public void setPageSize(String pageSize) {
PageSize = pageSize;
}
public String getPageIndex() {
return PageIndex;
}
public void setPageIndex(String pageIndex) {
PageIndex = pageIndex;
}
public String getPlanName() {
return planName;
}
public void setPlanName(String planName) {
this.planName = planName;
}
@Override
public String toString() {
return "ReportOpenApiRequest{" +
"ReportName='" + ReportName + '\'' +
", planName='" + planName + '\'' +
", PageIndex='" + PageIndex + '\'' +
", PageSize='" + PageSize + '\'' +
", SearchItems=" + SearchItems +
", ReportTableColNames='" + ReportTableColNames + '\'' +
", GroupItems=" + GroupItems +
", levelDisplay='" + levelDisplay + '\'' +
'}';
}
}
这个实体类里又有两个实体类的列表,我们也不要忘记给加上:
分别是ReportSearchItem:
package com.chanjet.magic.dto;
/**
* ClassName: ReportSearchItem
* Package: com.chanjet.magic.dto
* Description:
*
* @Author Zeiyalo
* @Create 2025/4/10 9:22
* @Version 1.0.0
*/
public class ReportSearchItem {
private String ColumnName;
private String BeginDefault;
private String BeginDefaultText;
private String EndDefault;
private String EndDefaultText;
// Getters 和 Setters
public String getColumnName() {
return ColumnName;
}
public void setColumnName(String columnName) {
ColumnName = columnName;
}
public String getBeginDefault() {
return BeginDefault;
}
public void setBeginDefault(String beginDefault) {
BeginDefault = beginDefault;
}
public String getBeginDefaultText() {
return BeginDefaultText;
}
public void setBeginDefaultText(String beginDefaultText) {
BeginDefaultText = beginDefaultText;
}
public String getEndDefault() {
return EndDefault;
}
public void setEndDefault(String endDefault) {
EndDefault = endDefault;
}
public String getEndDefaultText() {
return EndDefaultText;
}
public void setEndDefaultText(String endDefaultText) {
EndDefaultText = endDefaultText;
}
}
还有ReportGroupItem:
package com.chanjet.magic.dto;
/**
* ClassName: ReportGroupItem
* Package: com.chanjet.magic.dto
* Description:
*
* @Author Zeiyalo
* @Create 2025/4/10 9:22
* @Version 1.0.0
*/
public class ReportGroupItem {
private Boolean isGroup;
private String ColumnName;
private String grpFieldType;
private String dateFieldGrpStyle;
private Boolean canHorizontalLayout;
private Boolean isHorizontalLayout;
// Getters 和 Setters
public Boolean getIsGroup() {
return isGroup;
}
public void setIsGroup(Boolean isGroup) {
this.isGroup = isGroup;
}
public String getColumnName() {
return ColumnName;
}
public void setColumnName(String columnName) {
ColumnName = columnName;
}
public String getGrpFieldType() {
return grpFieldType;
}
public void setGrpFieldType(String grpFieldType) {
this.grpFieldType = grpFieldType;
}
public String getDateFieldGrpStyle() {
return dateFieldGrpStyle;
}
public void setDateFieldGrpStyle(String dateFieldGrpStyle) {
this.dateFieldGrpStyle = dateFieldGrpStyle;
}
public Boolean getCanHorizontalLayout() {
return canHorizontalLayout;
}
public void setCanHorizontalLayout(Boolean canHorizontalLayout) {
this.canHorizontalLayout = canHorizontalLayout;
}
public Boolean getIsHorizontalLayout() {
return isHorizontalLayout;
}
public void setIsHorizontalLayout(Boolean isHorizontalLayout) {
this.isHorizontalLayout = isHorizontalLayout;
}
}
然后这边弄完就要确定Content的类,也就是RequestBody中的数据,代码如下所示:
package com.chanjet.magic.request.content;
import com.chanjet.magic.dto.ReportOpenApiRequest;
import com.chanjet.openapi.sdk.java.AbstractChanjetContent;
import java.util.Objects;
/**
* ClassName: SaleDeliveryDetailReportContent
* Package: com.chanjet.magic.request.content
* Description:
*
* @Author Zeiyalo
* @Create 2025/4/10 10:16
* @Version 1.0.0
*/
public class SaleDeliveryDetailReportContent extends AbstractChanjetContent {
private static final long serialVersionUID = -5126668713954209528L;
private ReportOpenApiRequest request;
public SaleDeliveryDetailReportContent() {
}
public SaleDeliveryDetailReportContent(ReportOpenApiRequest request) {
this.request = request;
}
public ReportOpenApiRequest getRequest() {
return request;
}
public void setRequest(ReportOpenApiRequest request) {
this.request = request;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SaleDeliveryDetailReportContent)) return false;
SaleDeliveryDetailReportContent that = (SaleDeliveryDetailReportContent) o;
return Objects.equals(request, that.request);
}
@Override
public int hashCode() {
return Objects.hashCode(request);
}
@Override
public String toString() {
return "SaleDeliveryDetailReportContent{" +
"request=" + request +
'}';
}
public static SaleDeliveryDetailReportContentBuilder builder() { return SaleDeliveryDetailReportContent.builder(); }
public static class SaleDeliveryDetailReportContentBuilder {
private ReportOpenApiRequest request;
SaleDeliveryDetailReportContentBuilder() {
}
public SaleDeliveryDetailReportContentBuilder request(ReportOpenApiRequest request) {
this.request = request;
return this;
}
public SaleDeliveryDetailReportContent build() {
return new SaleDeliveryDetailReportContent(request);
}
@Override
public String toString() {
return "SaleDeliveryDetailReportContentBuilder{" +
"request=" + request +
'}';
}
}
}
弄完这个,request部分的数据就可以了,Response按照我之前的那种建立一个Response用于接收数据就可以了,可以看我往期文章;
三、修改具体业务实现通用
其实前面关于RequestBody赋值是类似的,就是修改了Controller层里面的东西,然后接收对象当时想着,反正response都是继承一个抽象类的(AbstractChanjetResponse),然后这个抽象类就是实现了两个接口(ChanjetResponse、Serializable),那我直接建个通用的,然后去取数就好了,于是尝试了一下,最后接口也测试成功了,后续再测测其他输入有没有问题吧!下面静静的看会代码:
cotroller层:
/**
* 通用报表类型查询
*
* @param username
* @param password
* @param map
* @return
* @throws ChanjetApiException
*/
@ApiOperation(value = "通用报表类型查询", httpMethod = "POST")
@PostMapping("/report/getBaseReport")
public BaseReportResponse getBaseReport(@RequestHeader("username") String username, @RequestHeader("password") String password, @RequestBody LinkedHashMap<String, Object> map) throws ChanjetApiException {
Object request = map.get("request");
BaseReportRequest baseReportRequest = new BaseReportRequest();
baseReportRequest.setAppKey(appKey);
baseReportRequest.setAppSecret(appSecret);
baseReportRequest.setOpenToken(accessToken);
baseReportRequest.setRequestUri("/tplus/api/v2/reportQuery/GetReportData");
baseReportRequest.setBizContent(new BaseReportContent(RequestParserUtils.parseRequest(request)));
return chanjetClient.execute(baseReportRequest);
}
其中涉及的类都在下面:
BaseReportResponse:
package com.chanjet.magic.response;
import com.chanjet.openapi.sdk.java.ChanjetResponse;
import java.io.Serializable;
import java.util.LinkedHashMap;
/**
* ClassName: BaseReportResponse
* Package: com.chanjet.magic.response
* Description:
*
* @Author Zeiyalo
* @Create 2025/4/10 15:19
* @Version 1.0.0
*/
public class BaseReportResponse extends LinkedHashMap<String, Object> implements ChanjetResponse, Serializable {
}
BaseReportRequest:
package com.chanjet.magic.request;
import com.chanjet.magic.response.BaseReportResponse;
import com.chanjet.magic.response.SaleDeliveryDetailReportResponse;
import com.chanjet.openapi.sdk.java.AbstractChanjetRequest;
import com.chanjet.openapi.sdk.java.enums.HttpMethod;
/**
* ClassName: BaseReportRequest
* Package: com.chanjet.magic.request
* Description:
*
* @Author Zeiyalo
* @Create 2025/4/10 15:20
* @Version 1.0.0
*/
public class BaseReportRequest extends AbstractChanjetRequest<BaseReportResponse> {
@Override
public Class<BaseReportResponse> getResponseClass() {
return BaseReportResponse.class;
}
@Override
public HttpMethod getHttpMethod() {
return HttpMethod.POST;
}
}
BaseReportContent:
package com.chanjet.magic.request.content;
import com.chanjet.magic.dto.ReportOpenApiRequest;
import com.chanjet.openapi.sdk.java.ChanjetContent;
import java.util.Objects;
/**
* ClassName: BaseReportContent
* Package: com.chanjet.magic.request.content
* Description:
*
* @Author Zeiyalo
* @Create 2025/4/10 11:52
* @Version 1.0.0
*/
public class BaseReportContent extends Object implements ChanjetContent {
private static final long serialVersionUID = -5126668713954209529L;
private ReportOpenApiRequest request;
public BaseReportContent() {
}
public BaseReportContent(ReportOpenApiRequest request) {
this.request = request;
}
public ReportOpenApiRequest getRequest() {
return request;
}
public void setRequest(ReportOpenApiRequest request) {
this.request = request;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BaseReportContent)) return false;
BaseReportContent that = (BaseReportContent) o;
return Objects.equals(request, that.request);
}
@Override
public int hashCode() {
return Objects.hashCode(request);
}
@Override
public String toString() {
return "BaseReportContent{" +
"request=" + request +
'}';
}
public static BaseReportContentBuilder builder() { return BaseReportContent.builder(); }
public static class BaseReportContentBuilder {
private ReportOpenApiRequest request;
BaseReportContentBuilder() {
}
public BaseReportContentBuilder request(ReportOpenApiRequest request) {
this.request = request;
return this;
}
public BaseReportContent build() {
return new BaseReportContent(request);
}
@Override
public String toString() {
return "BaseReportContentBuilder{" +
"request=" + request +
'}';
}
}
}
RequestParserUtils和上面一样的,开始也就说过了,请求参数是官网有的,所以直接按照官网的文档去建的;
测试成功如下图:
OKOK,差不多就到这里了,返回的数据有问题,我还是要去问问是不是我请求参数写错了,结果返回的数据都没有,再弄弄吧;