我们app因为是依赖于后台的,后台说什么就是什么,所以当后台定死了一个接口文档后都直接丢给我们不会与我们讨论解析如何复杂。因此作为强大的客户端开发,要自己去适配解析了。
比方说下面这个返回json格式。
{
"Ack": "GetPropertyValues",
"ID": "f33c475e-c7ee-94b2-6e77-5ad55de2a649",
"Status": "0",
"Params": [
{
"ObjectPath": "/com/*****/LANHosts/1",
"InterfaceName": "com.***.***.LANHost",
"Properties": [
{
"MAC": "E446DA6C6146"
},
{
"HostName": "MIX2-xiaomishouji"
},
{
"DevName": "MIX2-xiaomishouji"
},
{
"ControlStatus": false
},
{
"InternetAccess": 2
},
{
"StorageAccess": true
},
{
"MaxUSBandwidth": 0
},
{
"MaxDSBandwidth": 0
},
{
"DevType": "phone"
},
{
"IP": "192.168.1.2"
},
{
"ConnectionType": 1
},
{
"Port": 0
},
{
"Brand": "小米"
},
{
"Model": "小米MIX 2"
},
{
"OS": "android"
},
{
"Active": false
},
{
"LatestActiveTime": "2018-04-17 05:37:37"
},
{
"LatestInactiveTime": "2018-04-17 05:37:56"
},
{
"OnlineTime": 19
},
{
"RxBytes": 0
},
{
"TxBytes": 0
},
{
"PowerLevel": 0
},
{
"DeviceOnlineNofication": false
}
]
}
],
"SequenceId": "1234ABCD"
}
外层的都好解析,就是最里面那层Properties的参数,看中括号,是一个JsonArray,可是该JsonArray里的每一个JsonObject都不一样。因此我难道要创建十几个不同的JsonObject么。???!
用AndroidStudio中安装的plugin 插件GsonFormat 根据这段json生成的javabean如下所示。
public class RPCRetrunItem {
private String Ack;
private String ID;
private String Status;
private String SequenceId;
private List<ParamsBean> Params;
public String getAck() {
return Ack;
}
public void setAck(String Ack) {
this.Ack = Ack;
}
public String getID() {
return ID;
}
public void setID(String ID) {
this.ID = ID;
}
public String getStatus() {
return Status;
}
public void setStatus(String Status) {
this.Status = Status;
}
public String getSequenceId() {
return SequenceId;
}
public void setSequenceId(String SequenceId) {
this.SequenceId = SequenceId;
}
public List<ParamsBean> getParams() {
return Params;
}
public void setParams(List<ParamsBean> Params) {
this.Params = Params;
}
public static class ParamsBean {
private String ObjectPath;
private String InterfaceName;
private List<PropertiesBean> Properties;
public String getObjectPath() {
return ObjectPath;
}
public void setObjectPath(String ObjectPath) {
this.ObjectPath = ObjectPath;
}
public String getInterfaceName() {
return InterfaceName;
}
public void setInterfaceName(String InterfaceName) {
this.InterfaceName = InterfaceName;
}
public List<PropertiesBean> getProperties() {
return Properties;
}
public void setProperties(List<PropertiesBean> Properties) {
this.Properties = Properties;
}
public static class PropertiesBean {
private String MAC;
private String HostName;
private String DevName;
private boolean ControlStatus;
private int InternetAccess;
private boolean StorageAccess;
private int MaxUSBandwidth;
private int MaxDSBandwidth;
private String DevType;
private String IP;
private int ConnectionType;
private int Port;
private String Brand;
private String Model;
private String OS;
private boolean Active;
private String LatestActiveTime;
private String LatestInactiveTime;
private int OnlineTime;
private int RxBytes;
private int TxBytes;
private int PowerLevel;
private boolean DeviceOnlineNofication;
public String getMAC() {
return MAC;
}
public void setMAC(String MAC) {
this.MAC = MAC;
}
public String getHostName() {
return HostName;
}
public void setHostName(String HostName) {
this.HostName = HostName;
}
public String getDevName() {
return DevName;
}
public void setDevName(String DevName) {
this.DevName = DevName;
}
public boolean isControlStatus() {
return ControlStatus;
}
public void setControlStatus(boolean ControlStatus) {
this.ControlStatus = ControlStatus;
}
public int getInternetAccess() {
return InternetAccess;
}
public void setInternetAccess(int InternetAccess) {
this.InternetAccess = InternetAccess;
}
public boolean isStorageAccess() {
return StorageAccess;
}
public void setStorageAccess(boolean StorageAccess) {
this.StorageAccess = StorageAccess;
}
public int getMaxUSBandwidth() {
return MaxUSBandwidth;
}
public void setMaxUSBandwidth(int MaxUSBandwidth) {
this.MaxUSBandwidth = MaxUSBandwidth;
}
public int getMaxDSBandwidth() {
return MaxDSBandwidth;
}
public void setMaxDSBandwidth(int MaxDSBandwidth) {
this.MaxDSBandwidth = MaxDSBandwidth;
}
public String getDevType() {
return DevType;
}
public void setDevType(String DevType) {
this.DevType = DevType;
}
public String getIP() {
return IP;
}
public void setIP(String IP) {
this.IP = IP;
}
public int getConnectionType() {
return ConnectionType;
}
public void setConnectionType(int ConnectionType) {
this.ConnectionType = ConnectionType;
}
public int getPort() {
return Port;
}
public void setPort(int Port) {
this.Port = Port;
}
public String getBrand() {
return Brand;
}
public void setBrand(String Brand) {
this.Brand = Brand;
}
public String getModel() {
return Model;
}
public void setModel(String Model) {
this.Model = Model;
}
public String getOS() {
return OS;
}
public void setOS(String OS) {
this.OS = OS;
}
public boolean isActive() {
return Active;
}
public void setActive(boolean Active) {
this.Active = Active;
}
public String getLatestActiveTime() {
return LatestActiveTime;
}
public void setLatestActiveTime(String LatestActiveTime) {
this.LatestActiveTime = LatestActiveTime;
}
public String getLatestInactiveTime() {
return LatestInactiveTime;
}
public void setLatestInactiveTime(String LatestInactiveTime) {
this.LatestInactiveTime = LatestInactiveTime;
}
public int getOnlineTime() {
return OnlineTime;
}
public void setOnlineTime(int OnlineTime) {
this.OnlineTime = OnlineTime;
}
public int getRxBytes() {
return RxBytes;
}
public void setRxBytes(int RxBytes) {
this.RxBytes = RxBytes;
}
public int getTxBytes() {
return TxBytes;
}
public void setTxBytes(int TxBytes) {
this.TxBytes = TxBytes;
}
public int getPowerLevel() {
return PowerLevel;
}
public void setPowerLevel(int PowerLevel) {
this.PowerLevel = PowerLevel;
}
public boolean isDeviceOnlineNofication() {
return DeviceOnlineNofication;
}
public void setDeviceOnlineNofication(boolean DeviceOnlineNofication) {
this.DeviceOnlineNofication = DeviceOnlineNofication;
}
}
}
}
可是由于Properties的value是个List,而List中的每个对象都不同,因此这样 的javabean肯定是解析不出来的,用Gson去解析成该类时会报错。
所以。我们考虑将这种不规则的json格式强制转成客户端解析起来好调用的格式,下面上干货~
首先,我们将原有的JsonString拿到后进行正则表达式匹配,将Properties参数里面第一个“{ }”去掉 ,然后把里面每一个“},{”都替换成逗号“,”。拼接成一串符合我们调用方式的jsonString,
/**返回Json格式处理
* @param json
* @return
*/
public String processJson(String json){
String patternStr = "\"Properties\":\\[\\{[^]]*[\\]}]";
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(json);
StringBuffer sbr = new StringBuffer();
while (matcher.find()) {
String str3 = matcher.group();
// L.i("tags--0--",str3);
str3 = str3.replace("},{",",");
matcher.appendReplacement(sbr, str3);
}
matcher.appendTail(sbr);
L.i("tags--2--",sbr.toString());
return sbr.toString();
}
这样,输出来的json就变成了以下格式:
{
"Ack": "GetPropertyValues",
"ID": "28689eca-86d6-2f15-b9b8-5ad583c98b1b",
"Status": "0",
"Params": [{
"ObjectPath": "\/com\/c****/LANHosts\/1",
"InterfaceName": "com.****.LANHost",
"Properties": [{
"MAC": "E446DA6C6146",
"HostName": "MIX2-xiaomishouji",
"DevName": "MIX2-xiaomishouji",
"ControlStatus": false,
"InternetAccess": 2,
"StorageAccess": true,
"MaxUSBandwidth": 0,
"MaxDSBandwidth": 0,
"DevType": "phone",
"IP": "192.168.1.2",
"ConnectionType": 1,
"Port": 0,
"Brand": "u5c0fu7c73",
"Model": "u5c0fu7c73MIX 2",
"OS": "android",
"Active": false,
"LatestActiveTime": "2018-04-17 05:37:37",
"LatestInactiveTime": "2018-04-17 05:37:56",
"OnlineTime": 19,
"RxBytes": 0,
"TxBytes": 0,
"PowerLevel": 0,
"DeviceOnlineNofication": false
}]
}],
"SequenceId": "48213"
}
这样解决了第一步,看似是已经可以用gson来解析成我们需要的实体javabean了。可是在具体解析的过程中还是会报一个错误。
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.yueme.bean.router.ClientItemDT
这是因为我之前在构建javabean实体类时没有用一个类来封装,还是一层套一层用泛型类来解析的,所以在gson解析泛型类时无法识别里面的信息导致报错,这个时候,我要将JsonArray,也就是Properties后面的中括号内的东西改为JsonObject,变成一个实体类来解析。解决方法如下:
public static <T>RPCRetrurnDT<T> jsonToBean(String json, final Type T) {
Type resultType = new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[]{T};
}
@Override
public Type getOwnerType() {
return null;
}
@Override
public Type getRawType() {
return RPCRetrurnDT.class;
}
};
return getGson().fromJson(json, resultType);
}
将 之前第一步我们正则表达式修改好的jsonString传入到这个方法,并指定一个最终解析Properties内数据的实体类,就可以得到最外层解析成功的实体类了。
调用方式如下:
RPCRetrurnDT<ClientItemDT> rpcRetrurnDT = GsonUtil.jsonToBean(processJson(json), ClientItemDT.class);
其中,RPCReturnDT是最外层的基类。ClientItemDT是最里面的Properties的实体类。
public class RPCRetrurnDT<T> extends BaseDT {
public String Ack;
public String ID;
public String Status;
public String SequenceId;
public List<RPCParamsDT<T>> Params;
public String FailReason;
}
public class RPCParamsDT<T> extends BaseDT{
public String ObjectPath;
public String InterfaceName;
public List<T> Properties;
}
public class ClientItemDT extends BaseDT{
public String MAC;
public String HostName;
@SerializedName(value = "DN", alternate = "DevName")
public String DevName;//下挂终端的网络别名
@JsonAdapter(IntTypeAdapter.class)
@SerializedName(value = "CS", alternate = "ControlStatus")
public int ControlStatus;//true为正处于受控状态, false为未处于受控状态 1:true; 0:false
@SerializedName(value = "IA", alternate = "InternetAccess")
public String InternetAccess;//"OFF"-不允许接入(即拉黑);"ONInnerOnly"-允许接入但禁上网;"ON"-允许接入并且可以上网(对应:0,1,2)
@JsonAdapter(IntTypeAdapter.class)
@SerializedName(value = "SA", alternate = "StorageAccess")
public int StorageAccess;//是否启用USB存储设备的访问权限(ON表示开启、OFF表示不开启)(1,0)
@SerializedName(value = "MUB", alternate = "MaxUSBandwidth")
public long MaxUSBandwidth;//下挂设备上行最大带宽,0表示不限,单位为kbps
@SerializedName(value = "MDB", alternate = "MaxDSBandwidth")
public long MaxDSBandwidth;//下挂设备下行最大带宽,0表示不限,单位为kbps;
@SerializedName(value = "DT", alternate = "DevType")
public String DevType;//设备类型(phone/Pad/PC/STB/OTHER)
public String IP;//ip地址
@SerializedName(value = "CT", alternate = {"ConnectType","ConnectionType"})
@JsonAdapter(IntTypeAdapter.class)
public int ConnectType;//连接类型 1:无线 0:有线
@SerializedName(value = "Po", alternate = "Port")
public int Port;//具体连接端口(0:wifi连接,1:lan1口连接,2:lan2口连接,3:lan3口连接,4:lan4口连接,当ConnectionType=1时,表示SSIDIndex)
@SerializedName(value = "Br", alternate = "Brand")
public String Brand;//品牌
@SerializedName(value = "Mo", alternate = "Model")
public String Model;//型号
public String OS;//操作系统
@JsonAdapter(IntTypeAdapter.class)
@SerializedName(value = "Act", alternate = "Active")
public int Active;//下挂设备是否在线,在线=true,离线=false; 1:true; 0:false
@SerializedName(value = "LAT", alternate = "LatestActiveTime")
public String LatestActiveTime;//设备的最新的上线时间
@SerializedName(value = "LIT", alternate = "LatestInactiveTime")
public String LatestInactiveTime;//设备的最新的下线时间
@SerializedName(value = "OT", alternate = "OnlineTime")
public String OnlineTime;//设备在线时长;单位为秒
@SerializedName(value = "RB", alternate = "RxBytes")
public String RxBytes;//接收数据,单位byte
@SerializedName(value = "TB", alternate = "TxBytes")
public String TxBytes;//发送数据,单位byte
@SerializedName(value = "PL", alternate = "PowerLevel")
public String PowerLevel;//只在ConnetionType=1时有意义,表示此终端连接上的信号强度,单位dbm
@JsonAdapter(StringTypeAdapter.class)
@SerializedName(value = "DON", alternate = "DeviceOnlineNofication")
public String DeviceOnlineNofication;//下挂设备是否上报,默认值为false 1:true; 0:false
@SerializedName(value = "LTS", alternate = "LanhostTimerSet")
public String LanhostTimerSet;//下是否设置了上网时段限制 1:已设置; 0:未设置
以上就解决了后台数据Json不符合客户端调用习惯的坑,但是现在又有另一个坑,是我们应用是跟智能硬件相关的app,所以后台要升级智能硬件的时候,2.0和3.0的接口提供方式不同,所以我们要在不同的接口规范下和返回json不同的情况时做到页面上一次调用,也就是实体javabean要适应两种不同的接口json。大致参数差不多,但是由于一些具体字段的返回格式不一样,比如说 上面ClientItemDT中的
ControlStatus
字段,在2.0接口中返回的是int类型,但是在3.0接口中返回却是boolean型,因此我还得强制把返回的boolean转为int才能解析成功,具体操作方式可以参考我上一篇文章。
https://blog.csdn.net/danlyalex/article/details/79963304