一、概述

Android(Java) 平台已经有许多 Json 库了,包括 Google 推荐的 Gson,广受欢迎的 Jackson,阿里的 FastJson 等,但今天要说的是大名鼎鼎 Square 公司的 MoShi
轮子已经有很多了,不过自从 Google 将 Kotlin 定为 Android 亲儿子开发语言后,竟然包括 Gson 在内的几乎所有 Json 库均不支持,这当然可以理解,因为他们大多数采用了 Java 反射机制,注定难以适配 Kotlin 独有的特性。因此今天要介绍 Moshi —— 一个与 Kotlin 兼容极好的现代化解析库。可以很好的兼容 java 和 Kotlin。

二、java中使用

导入

  1. implementation 'com.squareup.moshi:moshi:1.12.0'

Bean类

  1. public class JavaBean {
  2. private String name;
  3. private String sex;
  4. private int age;
  5. public JavaBean(String name, String sex, int age) {
  6. this.name = name;
  7. this.sex = sex;
  8. this.age = age;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public String getSex() {
  17. return sex;
  18. }
  19. public void setSex(String sex) {
  20. this.sex = sex;
  21. }
  22. public int getAge() {
  23. return age;
  24. }
  25. public void setAge(int age) {
  26. this.age = age;
  27. }
  28. @Override
  29. public String toString() {
  30. return "JavaBean{" +
  31. "name='" + name + '\'' +
  32. ", sex='" + sex + '\'' +
  33. ", age=" + age +
  34. '}';
  35. }
  36. }

Bean

  • json -> bean

    1. private static void json2Bean() {
    2. String jj = "{\"age\":22,\"name\":\"alice\",\"sex\":\"man\"}";
    3. JsonAdapter<JavaBean> adapter = new Moshi.Builder().build().adapter(JavaBean.class);
    4. try {
    5. JavaBean bean = adapter.fromJson(jj);
    6. System.out.println(bean);
    7. } catch (IOException e) {
    8. e.printStackTrace();
    9. }
    10. }
  • bean -> json

    1. private static void bean2Json() {
    2. JavaBean javaBean = new JavaBean("alice", "man", 22);
    3. JsonAdapter<JavaBean> adapter = new Moshi.Builder().build().adapter(JavaBean.class);
    4. String json = adapter.toJson(javaBean);
    5. System.out.println(json);
    6. }

    Map

  • map -> json

    1. private static void map2Json() {
    2. Map<String, Integer> map = new HashMap<>();
    3. map.put("one", 1);
    4. map.put("two", 2);
    5. map.put("three", 3);
    6. Moshi moshi = new Moshi.Builder().build();
    7. ParameterizedType type = Types.newParameterizedType(Map.class, String.class, Integer.class);
    8. JsonAdapter<Object> adapter = moshi.adapter(type);
    9. String json = adapter.toJson(map);
    10. System.out.println(json);
    11. }
  • json -> map

    1. private static void json2Map() {
    2. String json = "{\"one\":1,\"two\":2,\"three\":3}";
    3. Moshi moshi = new Moshi.Builder().build();
    4. ParameterizedType type = Types.newParameterizedType(Map.class, String.class, Integer.class);
    5. JsonAdapter<Map<String, Integer>> adapter = moshi.adapter(type);
    6. try {
    7. Map<String, Integer> map = adapter.fromJson(json);
    8. System.out.println(map);
    9. } catch (IOException e) {
    10. e.printStackTrace();
    11. }
    12. }

    List

  • list -> json

    1. private static void list2Json() {
    2. ArrayList<JavaBean> list = new ArrayList<>();
    3. for (int i = 0; i < 3; i++) {
    4. list.add(new JavaBean("alice" + i, "man" + i, 23 + i));
    5. }
    6. Type listOfCardsType = Types.newParameterizedType(List.class, JavaBean.class);
    7. JsonAdapter<Object> adapter = new Moshi.Builder().build().adapter(listOfCardsType);
    8. String json = adapter.toJson(list);
    9. System.out.println(json);
    10. }
  • json -> list

    1. private static void json2list() {
    2. String json = "[{\"age\":23,\"name\":\"alice0\",\"sex\":\"man0\"},{\"age\":24,\"name\":\"alice1\",\"sex\":\"man1\"},{\"age\":25,\"name\":\"alice2\",\"sex\":\"man2\"}]";
    3. Type listOfCardsType = Types.newParameterizedType(List.class, JavaBean.class);
    4. JsonAdapter<List<JavaBean>> adapter = new Moshi.Builder().build().adapter(listOfCardsType);
    5. try {
    6. List<JavaBean> list = adapter.fromJson(json);
    7. System.out.println(list);
    8. } catch (IOException e) {
    9. e.printStackTrace();
    10. }
    11. }

    Others

  • @json:Key转换

  • transitent:跳过该字段不解析
  1. public final class Bean {
  2. @Json(name = "lucky number") int luckyNumber;
  3. @Json(name = "objec") int data;
  4. @Json(name = "toatl_price") String totolPrice;
  5. private transient int total;//jump the field
  6. }

三、Kotlin中使用

这里有两种,第一种是用反射的方式,不过这种方式有两个缺点,一是引入的包有点大,两一个就是对性能有影响。所以这里用注解的方式。
引入kapt:

  1. kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.12.0'

Bean类

  1. @JsonClass(generateAdapter = true)
  2. data class KotlinBean(var name: String, var age: Int)

或者下面这种:

  1. @JsonClass(generateAdapter = true)
  2. data class TestCode(
  3. @Json(name = "age")
  4. var age: Int,
  5. @Json(name = "name")
  6. var name: String
  7. ){
  8. fun isDTwty() = age > 20
  9. }

Bean

  1. @OptIn(ExperimentalStdlibApi::class)
  2. fun json2Bean() {
  3. val jsont = "{\"name\":\"alice\",\"age\":33}"
  4. val adapter = Moshi.Builder().build().adapter<KotlinBean>()
  5. println(adapter.fromJson(jsont))
  6. }
  7. @SuppressLint("CheckResult")
  8. @OptIn(ExperimentalStdlibApi::class)
  9. private fun bean2Json() {
  10. val adapter = Moshi.Builder().build().adapter<KotlinBean>()
  11. println(adapter.toJson(KotlinBean("alice", 33)))
  12. }

Map

  1. fun json2Map() {
  2. val json = "{\"name\":\"hello\",\"age\":99}"
  3. val types = Types.newParameterizedType(Map::class.java, String::class.java,Any::class.java)
  4. val adapter = Moshi.Builder().build().adapter<Map<String,Any>>(types)
  5. val map = adapter.fromJson(json)
  6. println(map?.get("name"))
  7. }
  8. fun map2Json() {
  9. val map = mapOf("name" to "hello","age" to 99)
  10. val types = Types.newParameterizedType(Map::class.java, String::class.java,Any::class.java)
  11. val adapter = Moshi.Builder().build().adapter<Map<String,Any>>(types)
  12. val json = adapter.toJson(map)
  13. println(json)
  14. }

List

  1. fun json2List() {
  2. println("-------------")
  3. val json = "[{\"name\":\"alice\",\"age\":22},{\"name\":\"bob\",\"age\":12}]"
  4. val type = Types.newParameterizedType(List::class.java,KotlinBean::class.java)
  5. val adapter = Moshi.Builder().build().adapter<List<KotlinBean>>(type)
  6. val list = adapter.fromJson(json)
  7. list?.forEach { println(it) }
  8. }
  9. fun list2Json() {
  10. val list = listOf(KotlinBean("alice",22),KotlinBean("bob",12))
  11. val type = Types.newParameterizedType(List::class.java,KotlinBean::class.java)
  12. val adapter = Moshi.Builder().build().adapter<List<KotlinBean>>(type)
  13. val json = adapter.toJson(list)
  14. println(json)
  15. }

Custom Type Adapters

  1. @JsonClass(generateAdapter = true)
  2. data class PersonAd(var name: String?, var age: Int,var sexAd: SexAd)
  3. enum class SexAd{
  4. MAN,WOMAN;
  5. }
  6. class SexAdapter{
  7. @FromJson
  8. fun fromJson(value: Int): SexAd{
  9. return if (value == 0) SexAd.MAN else SexAd.WOMAN
  10. }
  11. @ToJson
  12. fun toJson(sexAd: SexAd): Int{
  13. return if (sexAd == SexAd.MAN) 0 else 1
  14. }
  15. }
  16. class CommonAdapter{
  17. @FromJson
  18. fun fromJson(value: String?): String{
  19. //如果json数据没有这个字段,不执行这里
  20. println("fromJson--$value")
  21. return if (value.isNullOrEmpty()) "这是空" else value
  22. }
  23. @ToJson
  24. fun toJson(value: String?): String{
  25. return if (value.isNullOrEmpty()) "this is null" else value
  26. }
  27. }
  28. @ExperimentalStdlibApi
  29. fun main() {
  30. //测试SexAdapter
  31. /* val personAd = PersonAd("alice",99,SexAd.MAN)
  32. val moshi = Moshi.Builder().add(SexAdapter()).build()
  33. val adapter = moshi.adapter<PersonAd>()
  34. val json = adapter.toJson(personAd)
  35. println(json)
  36. val jj = "{\"name\":\"alice\",\"age\":99,\"sexAd\":1}"
  37. println("------${GsonKtx.toJson(personAd)}")
  38. val fromJson = adapter.fromJson(jj)
  39. println(fromJson)*/
  40. println("---------------")
  41. //测试通用CommonAdapter
  42. val personAd = PersonAd("",99,SexAd.MAN)
  43. val moshi = Moshi.Builder()
  44. .add(CommonAdapter())
  45. // .add(SexAdapter())
  46. .build()
  47. val adapter = moshi.adapter<PersonAd>()
  48. val json = adapter.toJson(personAd)
  49. println(json)
  50. val jj = "{\"name\":null,\"age\":99,\"sexAd\":\"MAN\"}"
  51. val person = adapter.fromJson(jj)
  52. println(person)
  53. }

Codegen

使用codegen会自动生成adpter,可以直接使用。导入kapt依赖,在bean类上加上@JsonClass(generateAdapter = true),示例如下:

  1. @JsonClass(generateAdapter = true)
  2. data class TestCode(
  3. @Json(name = "age")
  4. var age: Int,
  5. @Json(name = "name")
  6. var name: String
  7. ){
  8. fun isDTwty() = age > 20
  9. }

序列化和反序列化:

  1. val bean = TestCode(9,"moshi")
  2. println(TestCodeJsonAdapter(Moshi.Builder().build()).fromJson("{\"age\":9,\"name\":\"moshi\"}"))
  3. println(TestCodeJsonAdapter(Moshi.Builder().build()).toJson(bean))

四、封装代码

  1. object MSKtx {
  2. val moshi: Moshi = Moshi.Builder().build()
  3. @OptIn(ExperimentalStdlibApi::class)
  4. inline fun <reified T> toJson(t: T?): String? {
  5. if (t == null) return null
  6. return moshi.adapter<T>().toJson(t)
  7. }
  8. fun <T> fromJson(json: String?, type: Type): T? {
  9. if (json.isNullOrEmpty()) {
  10. return null
  11. }
  12. val adapter = moshi.adapter<T>(type)
  13. return adapter.fromJson(json)
  14. }
  15. @OptIn(ExperimentalStdlibApi::class)
  16. inline fun <reified T> toAny(json: String?): T? {
  17. if (json.isNullOrEmpty()) {
  18. return null
  19. }
  20. val adapter = moshi.adapter<T>()
  21. return adapter.fromJson(json)
  22. }
  23. inline fun <reified K, reified V> toMap(json: String?): MutableMap<K, V>? =
  24. fromJson(json, Types.newParameterizedType(Map::class.java, K::class.java, V::class.java))
  25. inline fun <reified T> toList(json: String?): MutableList<T>? =
  26. fromJson(json, Types.newParameterizedType(MutableList::class.java, T::class.java))
  27. }
  28. fun Any?.toJson() = MSKtx.toJson(this)
  29. fun <T> String?.fromJson(type: Type) = MSKtx.fromJson<T>(this, type)
  30. inline fun <reified T> String?.toAny() = MSKtx.toAny<T>(this)
  31. inline fun <reified K, reified V> String?.toMap() = MSKtx.toMap<K, V>(this)
  32. inline fun <reified T> String?.toList() = MSKtx.toList<T>(this)

使用示例:

java

  1. private static void skTest() {
  2. JavaBean javaBean = new JavaBean("alice", "man", 22);
  3. System.out.println(MSKtxKt.toJson(javaBean));
  4. String jj = "{\"age\":22,\"name\":\"alice\",\"sex\":\"man\"}";
  5. JavaBean bean = MSKtxKt.fromJson(jj, Types.newParameterizedType(JavaBean.class));
  6. System.out.println(bean);
  7. }

Kotlin

  1. println(MSKtx.toAny<TestCode>("{\"age\":9,\"name\":\"moshi\"}"))
  2. /*println(MSKtx.toJson(KotlinBean("alice", 33)))
  3. println(MSKtx.toJson(mapOf("name" to "hello","age" to 99)))
  4. println(MSKtx.toJson(listOf(KotlinBean("alice",22),KotlinBean("bob",12))))
  5. println("-------------")
  6. println(MSKtx.toAny<KotlinBean>("{\"name\":\"alice\",\"age\":33}"))
  7. println(MSKtx.toMap<String, Any>("{\"name\":\"hello\",\"age\":99}"))
  8. println(MSKtx.toList<KotlinBean>("[{\"name\":\"alice\",\"age\":22},{\"name\":\"bob\",\"age\":12}]"))*/

五、参考

新一代Json解析库Moshi使用及原理解析

Moshi with Kotlin Json 库—现代化的最佳损友