一、概述
Android(Java) 平台已经有许多 Json 库了,包括 Google 推荐的 Gson,广受欢迎的 Jackson,阿里的 FastJson 等,但今天要说的是大名鼎鼎 Square 公司的 MoShi 。
轮子已经有很多了,不过自从 Google 将 Kotlin 定为 Android 亲儿子开发语言后,竟然包括 Gson 在内的几乎所有 Json 库均不支持,这当然可以理解,因为他们大多数采用了 Java 反射机制,注定难以适配 Kotlin 独有的特性。因此今天要介绍 Moshi —— 一个与 Kotlin 兼容极好的现代化解析库。可以很好的兼容 java 和 Kotlin。
二、java中使用
导入
implementation 'com.squareup.moshi:moshi:1.12.0'
Bean类
public class JavaBean {private String name;private String sex;private int age;public JavaBean(String name, String sex, int age) {this.name = name;this.sex = sex;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "JavaBean{" +"name='" + name + '\'' +", sex='" + sex + '\'' +", age=" + age +'}';}}
Bean
json -> bean
private static void json2Bean() {String jj = "{\"age\":22,\"name\":\"alice\",\"sex\":\"man\"}";JsonAdapter<JavaBean> adapter = new Moshi.Builder().build().adapter(JavaBean.class);try {JavaBean bean = adapter.fromJson(jj);System.out.println(bean);} catch (IOException e) {e.printStackTrace();}}
bean -> json
private static void bean2Json() {JavaBean javaBean = new JavaBean("alice", "man", 22);JsonAdapter<JavaBean> adapter = new Moshi.Builder().build().adapter(JavaBean.class);String json = adapter.toJson(javaBean);System.out.println(json);}
Map
map -> json
private static void map2Json() {Map<String, Integer> map = new HashMap<>();map.put("one", 1);map.put("two", 2);map.put("three", 3);Moshi moshi = new Moshi.Builder().build();ParameterizedType type = Types.newParameterizedType(Map.class, String.class, Integer.class);JsonAdapter<Object> adapter = moshi.adapter(type);String json = adapter.toJson(map);System.out.println(json);}
json -> map
private static void json2Map() {String json = "{\"one\":1,\"two\":2,\"three\":3}";Moshi moshi = new Moshi.Builder().build();ParameterizedType type = Types.newParameterizedType(Map.class, String.class, Integer.class);JsonAdapter<Map<String, Integer>> adapter = moshi.adapter(type);try {Map<String, Integer> map = adapter.fromJson(json);System.out.println(map);} catch (IOException e) {e.printStackTrace();}}
List
list -> json
private static void list2Json() {ArrayList<JavaBean> list = new ArrayList<>();for (int i = 0; i < 3; i++) {list.add(new JavaBean("alice" + i, "man" + i, 23 + i));}Type listOfCardsType = Types.newParameterizedType(List.class, JavaBean.class);JsonAdapter<Object> adapter = new Moshi.Builder().build().adapter(listOfCardsType);String json = adapter.toJson(list);System.out.println(json);}
json -> list
private static void json2list() {String json = "[{\"age\":23,\"name\":\"alice0\",\"sex\":\"man0\"},{\"age\":24,\"name\":\"alice1\",\"sex\":\"man1\"},{\"age\":25,\"name\":\"alice2\",\"sex\":\"man2\"}]";Type listOfCardsType = Types.newParameterizedType(List.class, JavaBean.class);JsonAdapter<List<JavaBean>> adapter = new Moshi.Builder().build().adapter(listOfCardsType);try {List<JavaBean> list = adapter.fromJson(json);System.out.println(list);} catch (IOException e) {e.printStackTrace();}}
Others
@json:Key转换
- transitent:跳过该字段不解析
public final class Bean {@Json(name = "lucky number") int luckyNumber;@Json(name = "objec") int data;@Json(name = "toatl_price") String totolPrice;private transient int total;//jump the field}
三、Kotlin中使用
这里有两种,第一种是用反射的方式,不过这种方式有两个缺点,一是引入的包有点大,两一个就是对性能有影响。所以这里用注解的方式。
引入kapt:
kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.12.0'
Bean类
@JsonClass(generateAdapter = true)data class KotlinBean(var name: String, var age: Int)
或者下面这种:
@JsonClass(generateAdapter = true)data class TestCode(@Json(name = "age")var age: Int,@Json(name = "name")var name: String){fun isDTwty() = age > 20}
Bean
@OptIn(ExperimentalStdlibApi::class)fun json2Bean() {val jsont = "{\"name\":\"alice\",\"age\":33}"val adapter = Moshi.Builder().build().adapter<KotlinBean>()println(adapter.fromJson(jsont))}@SuppressLint("CheckResult")@OptIn(ExperimentalStdlibApi::class)private fun bean2Json() {val adapter = Moshi.Builder().build().adapter<KotlinBean>()println(adapter.toJson(KotlinBean("alice", 33)))}
Map
fun json2Map() {val json = "{\"name\":\"hello\",\"age\":99}"val types = Types.newParameterizedType(Map::class.java, String::class.java,Any::class.java)val adapter = Moshi.Builder().build().adapter<Map<String,Any>>(types)val map = adapter.fromJson(json)println(map?.get("name"))}fun map2Json() {val map = mapOf("name" to "hello","age" to 99)val types = Types.newParameterizedType(Map::class.java, String::class.java,Any::class.java)val adapter = Moshi.Builder().build().adapter<Map<String,Any>>(types)val json = adapter.toJson(map)println(json)}
List
fun json2List() {println("-------------")val json = "[{\"name\":\"alice\",\"age\":22},{\"name\":\"bob\",\"age\":12}]"val type = Types.newParameterizedType(List::class.java,KotlinBean::class.java)val adapter = Moshi.Builder().build().adapter<List<KotlinBean>>(type)val list = adapter.fromJson(json)list?.forEach { println(it) }}fun list2Json() {val list = listOf(KotlinBean("alice",22),KotlinBean("bob",12))val type = Types.newParameterizedType(List::class.java,KotlinBean::class.java)val adapter = Moshi.Builder().build().adapter<List<KotlinBean>>(type)val json = adapter.toJson(list)println(json)}
Custom Type Adapters
@JsonClass(generateAdapter = true)data class PersonAd(var name: String?, var age: Int,var sexAd: SexAd)enum class SexAd{MAN,WOMAN;}class SexAdapter{@FromJsonfun fromJson(value: Int): SexAd{return if (value == 0) SexAd.MAN else SexAd.WOMAN}@ToJsonfun toJson(sexAd: SexAd): Int{return if (sexAd == SexAd.MAN) 0 else 1}}class CommonAdapter{@FromJsonfun fromJson(value: String?): String{//如果json数据没有这个字段,不执行这里println("fromJson--$value")return if (value.isNullOrEmpty()) "这是空" else value}@ToJsonfun toJson(value: String?): String{return if (value.isNullOrEmpty()) "this is null" else value}}@ExperimentalStdlibApifun main() {//测试SexAdapter/* val personAd = PersonAd("alice",99,SexAd.MAN)val moshi = Moshi.Builder().add(SexAdapter()).build()val adapter = moshi.adapter<PersonAd>()val json = adapter.toJson(personAd)println(json)val jj = "{\"name\":\"alice\",\"age\":99,\"sexAd\":1}"println("------${GsonKtx.toJson(personAd)}")val fromJson = adapter.fromJson(jj)println(fromJson)*/println("---------------")//测试通用CommonAdapterval personAd = PersonAd("",99,SexAd.MAN)val moshi = Moshi.Builder().add(CommonAdapter())// .add(SexAdapter()).build()val adapter = moshi.adapter<PersonAd>()val json = adapter.toJson(personAd)println(json)val jj = "{\"name\":null,\"age\":99,\"sexAd\":\"MAN\"}"val person = adapter.fromJson(jj)println(person)}
Codegen
使用codegen会自动生成adpter,可以直接使用。导入kapt依赖,在bean类上加上@JsonClass(generateAdapter = true),示例如下:
@JsonClass(generateAdapter = true)data class TestCode(@Json(name = "age")var age: Int,@Json(name = "name")var name: String){fun isDTwty() = age > 20}
序列化和反序列化:
val bean = TestCode(9,"moshi")println(TestCodeJsonAdapter(Moshi.Builder().build()).fromJson("{\"age\":9,\"name\":\"moshi\"}"))println(TestCodeJsonAdapter(Moshi.Builder().build()).toJson(bean))
四、封装代码
object MSKtx {val moshi: Moshi = Moshi.Builder().build()@OptIn(ExperimentalStdlibApi::class)inline fun <reified T> toJson(t: T?): String? {if (t == null) return nullreturn moshi.adapter<T>().toJson(t)}fun <T> fromJson(json: String?, type: Type): T? {if (json.isNullOrEmpty()) {return null}val adapter = moshi.adapter<T>(type)return adapter.fromJson(json)}@OptIn(ExperimentalStdlibApi::class)inline fun <reified T> toAny(json: String?): T? {if (json.isNullOrEmpty()) {return null}val adapter = moshi.adapter<T>()return adapter.fromJson(json)}inline fun <reified K, reified V> toMap(json: String?): MutableMap<K, V>? =fromJson(json, Types.newParameterizedType(Map::class.java, K::class.java, V::class.java))inline fun <reified T> toList(json: String?): MutableList<T>? =fromJson(json, Types.newParameterizedType(MutableList::class.java, T::class.java))}fun Any?.toJson() = MSKtx.toJson(this)fun <T> String?.fromJson(type: Type) = MSKtx.fromJson<T>(this, type)inline fun <reified T> String?.toAny() = MSKtx.toAny<T>(this)inline fun <reified K, reified V> String?.toMap() = MSKtx.toMap<K, V>(this)inline fun <reified T> String?.toList() = MSKtx.toList<T>(this)
java
private static void skTest() {JavaBean javaBean = new JavaBean("alice", "man", 22);System.out.println(MSKtxKt.toJson(javaBean));String jj = "{\"age\":22,\"name\":\"alice\",\"sex\":\"man\"}";JavaBean bean = MSKtxKt.fromJson(jj, Types.newParameterizedType(JavaBean.class));System.out.println(bean);}
Kotlin
println(MSKtx.toAny<TestCode>("{\"age\":9,\"name\":\"moshi\"}"))/*println(MSKtx.toJson(KotlinBean("alice", 33)))println(MSKtx.toJson(mapOf("name" to "hello","age" to 99)))println(MSKtx.toJson(listOf(KotlinBean("alice",22),KotlinBean("bob",12))))println("-------------")println(MSKtx.toAny<KotlinBean>("{\"name\":\"alice\",\"age\":33}"))println(MSKtx.toMap<String, Any>("{\"name\":\"hello\",\"age\":99}"))println(MSKtx.toList<KotlinBean>("[{\"name\":\"alice\",\"age\":22},{\"name\":\"bob\",\"age\":12}]"))*/
