/*Copyright (C) 2026 the APIJSON group. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
package apijson;
import java.util.*;
/**parser for response
* @author Lemon
* @see #getObject
* @see #getList
* @use JSONResponse response = new JSONResponse(json);
* User user = response.getObject(User.class);//not a must
* List commenntList = response.getList("Comment[]", Comment.class);//not a must
*/
public interface JSONResponse, L extends List> extends JSONMap {
static final String TAG = "JSONResponse";
// è约æ§è½ååå° bugï¼é¤äºå
³é®è¯ @key ï¼ä¸è¬é½ç¬¦ååéå½åè§èï¼ä¸ç¬¦åä¹åæ ·è¿å便äºè°è¯
/**æ ¼å¼å带 - 䏿¨ªçº¿çåè¯
*/
public static boolean IS_FORMAT_HYPHEN = false;
/**æ ¼å¼å带 _ ä¸å线çåè¯
*/
public static boolean IS_FORMAT_UNDERLINE = false;
/**æ ¼å¼å带 $ ç¾å
符çåè¯
*/
public static boolean IS_FORMAT_DOLLAR = false;
//default JSONResponse() {
// super();
//}
//default JSONResponse(Object json) {
// this(parseObject(json));
//}
//default JSONResponse(Object json, JSONParser parser) {
// this(parseObject(json, parser));
//}
//default JSONResponse(Map object) {
// super(format(object));
//}
//default JSONResponse(M object, JSONCreator creator) {
// super(format(object, creator));
//}
//ç¶æä¿¡æ¯ï¼éGET请æ±è·å¾çä¿¡æ¯<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public static final int CODE_SUCCESS = 200; //æå
public static final int CODE_UNSUPPORTED_ENCODING = 400; //ç¼ç é误
public static final int CODE_ILLEGAL_ACCESS = 401; //æéé误
public static final int CODE_UNSUPPORTED_OPERATION = 403; //ç¦æ¢æä½
public static final int CODE_NOT_FOUND = 404; //æªæ¾å°
public static final int CODE_ILLEGAL_ARGUMENT = 406; //åæ°é误
public static final int CODE_NOT_LOGGED_IN = 407; //æªç»å½
public static final int CODE_TIME_OUT = 408; //è¶
æ¶
public static final int CODE_CONFLICT = 409; //éå¤ï¼å·²åå¨
public static final int CODE_CONDITION_ERROR = 412; //æ¡ä»¶é误ï¼å¦å¯ç é误
public static final int CODE_UNSUPPORTED_TYPE = 415; //ç±»åé误
public static final int CODE_OUT_OF_RANGE = 416; //è¶
åºèå´
public static final int CODE_NULL_POINTER = 417; //对象为空
public static final int CODE_SERVER_ERROR = 500; //æå¡å¨å
é¨é误
public static final String MSG_SUCCEED = "success"; //æå
public static final String MSG_SERVER_ERROR = "Internal Server Error!"; //æå¡å¨å
é¨é误
public static String KEY_OK = "ok";
public static String KEY_CODE = "code";
public static String KEY_MSG = "msg";
public static final String KEY_COUNT = "count";
public static final String KEY_TOTAL = "total";
public static final String KEY_INFO = "info"; //详ç»çå页信æ¯
public static final String KEY_FIRST = "first"; //æ¯å¦ä¸ºé¦é¡µ
public static final String KEY_LAST = "last"; //æ¯å¦ä¸ºå°¾é¡µ
public static final String KEY_MAX = "max"; //æå¤§é¡µç
public static final String KEY_MORE = "more"; //æ¯å¦ææ´å¤
/**è·åç¶æ
* @return
*/
default int getCode() {
try {
return JSON.getIntValue(this, KEY_CODE);
} catch (Exception e) {
//empty
}
return 0;
}
/**è·åç¶æ
* @return
*/
public static int getCode(Map reponse) {
try {
return JSON.getIntValue(reponse, KEY_CODE);
} catch (Exception e) {
//empty
}
return 0;
}
/**è·åç¶ææè¿°
* @return
*/
default String getMsg() {
return JSON.getString(this, KEY_MSG);
}
/**è·åç¶ææè¿°
* @param response
* @return
*/
public static String getMsg(Map response) {
return response == null ? null : JSON.getString(response, KEY_MSG);
}
/**è·åid
* @return
*/
default long getId() {
try {
return JSON.getLongValue(this, getIdKey());
} catch (Exception e) {
//empty
}
return 0;
}
/**è·åæ°é
* @return
*/
default int getCount() {
try {
return JSON.getIntValue(this, KEY_COUNT);
} catch (Exception e) {
//empty
}
return 0;
}
/**è·åæ»æ°
* @return
*/
default int getTotal() {
try {
return JSON.getIntValue(this, KEY_TOTAL);
} catch (Exception e) {
//empty
}
return 0;
}
/**æ¯å¦æå
* @return
*/
default boolean isSuccess() {
return isSuccess(getCode());
}
/**æ¯å¦æå
* @param code
* @return
*/
public static boolean isSuccess(int code) {
return code == CODE_SUCCESS;
}
/**æ¯å¦æå
* @param response
* @return
*/
public static boolean isSuccess(JSONResponse, ?> response) {
return response != null && response.isSuccess();
}
/**æ¯å¦æå
* @param response
* @return
*/
public static boolean isSuccess(Map response) {
return response != null && isSuccess(JSON.getIntValue(response, KEY_CODE));
}
/**æ ¡éªæå¡ç«¯æ¯å¦åå¨table
* @return
*/
default boolean isExist() {
return isExist(getCount());
}
/**æ ¡éªæå¡ç«¯æ¯å¦åå¨table
* @param count
* @return
*/
public static boolean isExist(int count) {
return count > 0;
}
/**æ ¡éªæå¡ç«¯æ¯å¦åå¨table
* @param response
* @return
*/
public static boolean isExist(JSONResponse, ?> response) {
return response != null && response.isExist();
}
public static boolean isExist(Map response) {
return response != null && isExist(JSON.getIntValue(response, KEY_COUNT));
}
/**è·åå
é¨çJSONResponse
* @param key
* @return
*/
default JSONResponse getJSONResponse(String key) {
return getObject(key, JSONResponse.class);
}
//cannot get javaBeanDeserizer
// /**è·åå
é¨çJSONResponse
// * @param response
// * @param key
// * @return
// */
// public static JSONResponse getJSONResponse(JSONRequest response, String key) {
// return response == null ? null : response.getObject(key, JSONResponse.class);
// }
//ç¶æä¿¡æ¯ï¼éGET请æ±è·å¾çä¿¡æ¯>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
/**
* key = clazz.getSimpleName()
* @param clazz
* @return
*/
default T getObject(Class clazz) {
return getObject(clazz == null ? "" : clazz.getSimpleName(), clazz);
}
/**
* @param key
* @param clazz
* @return
*/
default T getObject(String key, Class clazz) {
return getObject(this, key, clazz);
}
/**
* @param object
* @param key
* @param clazz
* @return
*/
public static T getObject(
Map object, String key, Class clazz) {
return toObject(object == null ? null : JSON.get(object, formatObjectKey(key)), clazz);
}
/**
* @param clazz
* @return
*/
default T toObject(Class clazz) {
return toObject(this, clazz);
}
/**
* @param object
* @param clazz
* @return
*/
public static , L extends List> T toObject(
Map object, Class clazz) {
return JSON.parseObject(object, clazz);
}
/**
* arrayObject = this
* @param key
* @return
*/
default List getList(String key) {
return JSON.getList(this, key);
}
/**
* key = KEY_ARRAY
* @param object
* @return
*/
public static List getList(Map object) {
return JSON.getList(object, KEY_ARRAY);
}
/**
* @param object
* @param key
* @param clazz
* @return
*/
public static > List getList(Map object, String key, Class clazz) {
return object == null ? null : JSON.parseArray(JSON.getString(object, formatArrayKey(key)), clazz);
}
/**
* key = KEY_ARRAY
* @return
*/
default > L getArray() {
return getArray(KEY_ARRAY);
}
/**
* @param key
* @return
*/
default > L getArray(String key) {
return getArray(this, key);
}
/**
* @param object
* @return
*/
public static > L getArray(Map object) {
return getArray(object, KEY_ARRAY);
}
/**
* key = KEY_ARRAY
* @param object
* @param key
* @return
*/
public static > L getArray(Map object, String key) {
return object == null ? null : JSON.get(object, formatArrayKey(key));
}
// /**
// * @return
// */
// default JSONRequest format() {
// return format(this);
// }
/**æ ¼å¼åkeyåç§°
* @param object
* @return
*/
public static , L extends List> M format(final M object) {
//å¤ªé¿æ¥ç䏿¹ä¾¿ï¼ä¸å¦debug Log.i(TAG, "format object = \n" + JSON.toJSONString(object));
if (object == null || object.isEmpty()) {
Log.i(TAG, "format object == null || object.isEmpty() >> return object;");
return object;
}
M formatedObject = JSON.createJSONObject();
Set set = object.keySet();
if (set != null) {
Object value;
for (String key : set) {
value = object.get(key);
if (value instanceof List>) {//JSONListï¼é忥formatå
é¨é¡¹
formatedObject.put(formatArrayKey(key), format((L) value));
}
else if (value instanceof Map, ?>) {//JSONRequestï¼å¾ä¸ä¸çº§æå
formatedObject.put(formatObjectKey(key), format((M) value));
}
else {//å
¶å®Objectï¼ç´æ¥å¡«å
formatedObject.put(formatOtherKey(key), value);
}
}
}
//å¤ªé¿æ¥ç䏿¹ä¾¿ï¼ä¸å¦debug Log.i(TAG, "format return formatedObject = " + JSON.toJSONString(formatedObject));
return formatedObject;
}
/**æ ¼å¼åkeyåç§°
* @param array
* @return
*/
public static , L extends List> L format(final L array) {
//å¤ªé¿æ¥ç䏿¹ä¾¿ï¼ä¸å¦debug Log.i(TAG, "format array = \n" + JSON.toJSONString(array));
if (array == null || array.isEmpty()) {
Log.i(TAG, "format array == null || array.isEmpty() >> return array;");
return array;
}
L formattedArray = JSON.createJSONArray();
Object value;
for (int i = 0; i < array.size(); i++) {
value = array.get(i);
if (value instanceof List>) {//JSONListï¼é忥formatå
é¨é¡¹
formattedArray.add(format((L) value));
}
else if (value instanceof Map, ?>) {//JSONRequestï¼å¾ä¸ä¸çº§æå
formattedArray.add(format((M) value));
}
else {//å
¶å®Objectï¼ç´æ¥å¡«å
formattedArray.add(value);
}
}
//å¤ªé¿æ¥ç䏿¹ä¾¿ï¼ä¸å¦debug Log.i(TAG, "format return formattedArray = " + JSON.toJSONString(formattedArray));
return formattedArray;
}
/**è·å表åç§°
* @param fullName name æ name:alias
* @return name => name; name:alias => alias
*/
public static String getTableName(String fullName) {
//key:alias -> alias; key:alias[] -> alias[]
int index = fullName == null ? -1 : fullName.indexOf(":");
return index < 0 ? fullName : fullName.substring(0, index);
}
/**è·ååéå
* @param fullName
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean, boolean, Boolean)} formatColon = true, formatAt = true, formatHyphen = true, firstCase = true
*/
public static String getVariableName(String fullName) {
if (JSONMap.isArrayKey(fullName)) {
fullName = StringUtil.addSuffix(fullName.substring(0, fullName.length() - 2), "list");
}
return formatKey(fullName, true, true, true, true, false, true);
}
/**æ ¼å¼åæ°ç»çåç§° key[] => keyList; key:alias[] => aliasList; Table-column[] => tableColumnList
* @param key empty ? "list" : key + "List" ä¸é¦åæ¯å°å
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean, boolean, Boolean)} formatColon = false, formatAt = true, formatHyphen = true, firstCase = true
*/
public static String formatArrayKey(String key) {
if (JSONMap.isArrayKey(key)) {
key = StringUtil.addSuffix(key.substring(0, key.length() - 2), "list");
}
int index = key == null ? -1 : key.indexOf(":");
if (index >= 0) {
return key.substring(index + 1); //ä¸å¤çèªå®ä¹ç
}
return formatKey(key, false, true, true, IS_FORMAT_UNDERLINE, IS_FORMAT_DOLLAR, false); //è约æ§è½ï¼é¤äºæ°ç»å¯¹è±¡ Table-column:alias[] ï¼ä¸è¬é½ç¬¦ååéå½åè§è
}
/**æ ¼å¼å对象çåç§° name => name; name:alias => alias
* @param key name æ name:alias
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean, boolean, Boolean)} formatColon = false, formatAt = true, formatHyphen = false, firstCase = true
*/
public static String formatObjectKey(String key) {
int index = key == null ? -1 : key.indexOf(":");
if (index >= 0) {
return key.substring(index + 1); // ä¸å¤çèªå®ä¹ç
}
return formatKey(key, false, true, IS_FORMAT_HYPHEN, IS_FORMAT_UNDERLINE, IS_FORMAT_DOLLAR, false); //è约æ§è½ï¼é¤äºè¡¨å¯¹è±¡ Table:alias ï¼ä¸è¬é½ç¬¦ååéå½åè§è
}
/**æ ¼å¼åæ®éå¼çåç§° name => name; name:alias => alias
* @param fullName name æ name:alias
* @return {@link #formatKey(String, boolean, boolean, boolean, boolean, boolean, Boolean)} formatColon = false, formatAt = true, formatHyphen = false, firstCase = false
*/
public static String formatOtherKey(String fullName) {
return formatKey(fullName, false, true, IS_FORMAT_HYPHEN, IS_FORMAT_UNDERLINE, IS_FORMAT_DOLLAR
, IS_FORMAT_HYPHEN || IS_FORMAT_UNDERLINE || IS_FORMAT_DOLLAR ? false : null);
}
/**æ ¼å¼ååç§°
* @param fullName name æ name:alias
* @param formatAt å»é¤åç¼ @ ï¼ @a => a
* @param formatColon å»é¤åé符 : ï¼ A:b => b
* @param formatHyphen å»é¤åé符 - ï¼ A-b-cd-Efg => aBCdEfg
* @param formatUnderline å»é¤åé符 _ ï¼ A_b_cd_Efg => aBCdEfg
* @param formatDollar å»é¤åé符 $ ï¼ A$b$cd$Efg => aBCdEfg
* @param firstCase 第ä¸ä¸ªåè¯é¦åæ¯å°åï¼åé¢çé¦åæ¯å¤§åï¼ Ab => ab ; A-b-Cd => aBCd
* @return name => name; name:alias => alias
*/
public static String formatKey(String fullName, boolean formatColon, boolean formatAt, boolean formatHyphen
, boolean formatUnderline, boolean formatDollar, Boolean firstCase) {
if (fullName == null) {
Log.w(TAG, "formatKey fullName == null >> return null;");
return null;
}
if (formatColon) {
fullName = formatColon(fullName);
}
if (formatAt) { //å
³é®è¯åªå»æåç¼ï¼ä¸æ ¼å¼ååè¯ï¼ä¾å¦ @a-b è¿å a-b ï¼æåä¸ä¼è°ç¨ setter
fullName = formatAt(fullName);
}
if (formatHyphen && fullName.contains("-")) {
fullName = formatHyphen(fullName, true);
}
if (formatUnderline && fullName.contains("_")) {
fullName = formatUnderline(fullName, true);
}
if (formatDollar && fullName.contains("$")) {
fullName = formatDollar(fullName, true);
}
// é»è®¤ä¸æ ¼å¼åæ®é key:value (value ä¸ä¸º [], {}) ç key
return firstCase == null ? fullName : StringUtil.firstCase(fullName, firstCase);
}
/**"@key" => "key"
* @param key
* @return
*/
public static String formatAt(@NotNull String key) {
return key.startsWith("@") ? key.substring(1) : key;
}
/**key:alias => alias
* @param key
* @return
*/
public static String formatColon(@NotNull String key) {
int index = key.indexOf(":");
return index < 0 ? key : key.substring(index + 1);
}
/**A-b-cd-Efg => ABCdEfg
* @param key
* @return
*/
public static String formatHyphen(@NotNull String key) {
return StringUtil.firstCase(formatHyphen(key, true), false);
}
/**A-b-cd-Efg => ABCdEfg
* @param key
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatHyphen(@NotNull String key, Boolean firstCase) {
return formatHyphen(key, firstCase, false);
}
/**A-b-cd-Efg => ABCdEfg
* @param key
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @param otherCase éé¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatHyphen(@NotNull String key, Boolean firstCase, Boolean otherCase) {
return formatDivider(key, "-", firstCase, otherCase);
}
/**A_b_cd_Efg => ABCdEfg
* @param key
* @return
*/
public static String formatUnderline(@NotNull String key) {
return StringUtil.firstCase(formatUnderline(key, true), false);
}
/**A_b_cd_Efg => ABCdEfg
* @param key
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatUnderline(@NotNull String key, Boolean firstCase) {
return formatUnderline(key, firstCase, false);
}
/**A_b_cd_Efg => ABCdEfg
* @param key
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @param otherCase éé¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatUnderline(@NotNull String key, Boolean firstCase, Boolean otherCase) {
return formatDivider(key, "_", firstCase, otherCase);
}
/**A$b$cd$Efg => ABCdEfg
* @param key
* @return
*/
public static String formatDollar(@NotNull String key) {
return StringUtil.firstCase(formatDollar(key, true), false);
}
/**A$b$cd$Efg => ABCdEfg
* @param key
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatDollar(@NotNull String key, Boolean firstCase) {
return formatDollar(key, firstCase, false);
}
/**A$b$cd$Efg => ABCdEfg
* @param key
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @param otherCase éé¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatDollar(@NotNull String key, Boolean firstCase, Boolean otherCase) {
return formatDivider(key, "$", firstCase, otherCase);
}
/**A.b.cd.Efg => ABCdEfg
* @param key
* @return
*/
public static String formatDot(@NotNull String key) {
return StringUtil.firstCase(formatDot(key, true), false);
}
/**A.b.cd.Efg => ABCdEfg
* @param key
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatDot(@NotNull String key, Boolean firstCase) {
return formatDot(key, firstCase, false);
}
/**A.b.cd.Efg => ABCdEfg
* @param key
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @param otherCase éé¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatDot(@NotNull String key, Boolean firstCase, Boolean otherCase) {
return formatDivider(key, ".", firstCase, otherCase);
}
/**A/b/cd/Efg => ABCdEfg
* @param key
* @return
*/
public static String formatDivider(@NotNull String key, Boolean firstCase) {
return formatDivider(key, "/", firstCase);
}
/**å»é¤åå²ç¬¦ï¼è¿åé©¼å³°æ ¼å¼
* @param key
* @param divider
* @return
*/
public static String formatDivider(@NotNull String key, @NotNull String divider) {
return StringUtil.firstCase(formatDivider(key, divider, true), false);
}
/**å»é¤åå²ç¬¦ï¼è¿åé©¼å³°æ ¼å¼
* @param key
* @param divider
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatDivider(@NotNull String key, @NotNull String divider, Boolean firstCase) {
return formatDivider(key, divider, firstCase, false);
}
/**å»é¤åå²ç¬¦ï¼è¿åé©¼å³°æ ¼å¼
* @param key
* @param divider
* @param firstCase é¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @param otherCase éé¦å符ç大å°åï¼true-大åï¼false-å°åï¼null-ä¸å¤ç
* @return
*/
public static String formatDivider(@NotNull String key, @NotNull String divider, Boolean firstCase, Boolean otherCase) {
String[] parts = StringUtil.split(key, divider);
StringBuilder name = new StringBuilder();
for (String part : parts) {
if (otherCase != null) {
part = otherCase ? part.toUpperCase() : part.toLowerCase();
}
if (firstCase != null) {
part = StringUtil.firstCase(part, firstCase);
}
name.append(part);
}
return name.toString();
}
}