moshi

moshi 的使用方法简介,项目地址

1、通过 JsonClass 解析 Kotlin

// build.gradle 依赖
id 'kotlin-kapt'

kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.14.0'
// @JsonClass 注解
@JsonClass(generateAdapter = true) data class PlantParserData(
    val plantId: String,
    val name: String,
    val description: String,
    val growZoneNumber: Int,
    val wateringInterval: Int,
    val imageUrl: String,
)

fun parserJsonClass() {
    val moshi = Moshi.Builder().build()

    val jsonAdapter: JsonAdapter<PlantParserData> = moshi.adapter(PlantParserData::class.java)
    val jsonData = jsonAdapter.fromJson(dataKotlinParser)

    log(jsonData.toString())

    log(jsonAdapter.toJson(jsonData))
}

2、通过 KotlinJsonAdapterFactory 解析 Kotlin

// build.gradle 依赖
implementation "com.squareup.moshi:moshi-kotlin:1.14.0"
data class PlantKotlinData(
    val plantId: String,
    val name: String,
    val description: String,
    val growZoneNumber: Int,
    val wateringInterval: Int,
    val imageUrl: String,
)

fun parserKotlinData() {
    val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()

    val jsonAdapter: JsonAdapter<PlantKotlinData> = moshi.adapter(PlantKotlinData::class.java)
    val jsonData = jsonAdapter.fromJson(dataKotlinParser)

    log(jsonData.toString())

    log(jsonAdapter.toJson(jsonData))
}

3、newParameterizedType 解析 List

fun parserJsonList(){
    val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
    val plantListType = Types.newParameterizedType(List::class.java, PlantKotlinData::class.java)
    val plantListAdapter = moshi.adapter<List<PlantKotlinData>>(plantListType)

    val plantList = plantListAdapter.fromJson(dataList) ?: emptyList()

    log(plantList.toString())

    log(plantListAdapter.toJson(plantList))
}

4、indent 格式化打印

fun printPretty() {
    val plant = PlantKotlinData(
        "coriandrum-sativum",
        "Cilantro",
        "Coriander",
        2,
        4,
        "A_scene_of_Coriander_leaves.JPG"
    )

    val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
    val plantAdapter = moshi.adapter(PlantKotlinData::class.java)

    logJson<PlantKotlinData>(plant,plantAdapter)
}

5、InputSteam 流的解析

// import okio.buffer
// import okio.source
suspend fun inputStream(context: Context){
    coroutineScope {
        try {
            val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()

            val inputStream = context.assets.open("plants.json")

            val plantListType = Types.newParameterizedType(List::class.java, Plant::class.java)
            val plantListAdapter = moshi.adapter<List<Plant>>(plantListType)

            val plantList = plantListAdapter.fromJson(inputStream.source().buffer()) ?: emptyList()
            logJson<List<Plant>>(plantList,plantListAdapter)
        }catch (e:Exception){
           log("数据流解析失败")
        }
    }

}

6、Kotlin OutputSteam

  • 对象以 JSON 的数据 写入文件
  • 泛型的的转换
fun <T> outStream(obj: T, context: Context) {
    val file = File(context.cacheDir, "textKotlin.txt")
    if (!file.exists()) file.createNewFile()
    log("${file.absolutePath} : ${file.exists()}")
    val fileOutputStream = FileOutputStream(file)
    val moshi = Moshi.Builder().build()
    
    val adapter = moshi.adapter(obj!!::class.java as Class<T>?) //根据泛型获取adapter
    
    val out = fileOutputStream.sink().buffer() //OutputStream 转化为 Sink.buffer
    adapter.toJson(out, obj) //对象数据的写入
    
    out.flush()
    log("kotlin end")
}

7、Java OutputSteam

  • 对象以 JSON 的数据 写入文件
  • 泛型的的转换
public static <T> void outStream(T obj, Context context)  {
    try {
        File file = new File(context.getCacheDir(),"text.txt");
        if(!file.exists()) file.createNewFile();
        log(file.getAbsolutePath() +":" +file.exists());
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        Moshi moshi = new Moshi.Builder().build();

        //根据泛型获取adapter
        JsonAdapter<T> adapter = moshi.adapter((Class<T>) obj.getClass());

        //OutputStream 转化为 Sink.buffer
        BufferedSink out = Okio.buffer(Okio.sink(fileOutputStream));

        //对象数据的写入
        adapter.toJson(out, obj);

        out.flush();
        log("java end");
    }catch (Exception e){
        Log.e("moshi",  e.toString());
    }
}

8、自定义 json Adapter

class CardAdapter {

    @ToJson
    fun toJson(card: Card) = card.rank + card.suit.toString()

    @FromJson
    fun fromJson(card: String): Card {
        if (card.length != 2) throw JsonDataException("Unknown card: $card")
        val rank = card[0]
        return when (card[1]) {
            'C' -> Card(rank, Suit.CLUBS)
            'D' -> Card(rank, Suit.DIAMONDS)
            'H' -> Card(rank, Suit.HEARTS)
            'S' -> Card(rank, Suit.SPADES)
            else -> throw JsonDataException("unknown suit: $card")
        }
    }
}

fun parserCustom(){
    val text = """
        {
          "hidden_card": "6S",
          "visible_cards":[
            "4C",
            "AH"
          ]
        }
    """.trimIndent()

    val moshi = Moshi.Builder().add(CardAdapter()).add(KotlinJsonAdapterFactory()).build()
    val jsonAdapter = moshi.adapter(BlackjackHand::class.java)

    val blackjackHand = jsonAdapter.fromJson(text)
    log(blackjackHand.toString())
}

9、base64 转换

fun base64Decode() {
    val json = "\"TW9zaGksIE9saXZlLCBXaGl0ZSBDaGluPw\""

    val moshi = Moshi.Builder().add(ByteString::class.java, Base64ByteStringAdapter()).build()
    val jsonAdapter: JsonAdapter<ByteString> = moshi.adapter(ByteString::class.java)

    val byteString: ByteString = jsonAdapter.fromJson(json)
    log(byteString.toString()) //输出 [text=Moshi, Olive, White Chin?]
}

class Base64ByteStringAdapter : JsonAdapter<ByteString>() {
    override fun fromJson(reader: JsonReader): ByteString? {
        val base64: String = reader.nextString()
        println(base64)
        return base64.decodeBase64()
    }

    override fun toJson(writer: JsonWriter, value: ByteString?) {
        val string = value?.base64()
        writer.value(string)
    }
}

10、自定义 AdapterFactory

fun customAdapterFactory(){
    val moshi = Moshi.Builder().add(SortedSetAdapterFactory()).build()
    val jsonAdapter: JsonAdapter<SortedSet<String>> = moshi.adapter(
        Types.newParameterizedType(
            SortedSet::class.java, String::class.java
        )
    )

    val model: TreeSet<String> = TreeSet()
    model.add("a")
    model.add("b")
    model.add("d")
    model.add("c")

    val json = jsonAdapter.toJson(model)
    log(json)
}

class SortedSetAdapter<T>(private val elementAdapter:JsonAdapter<T>): JsonAdapter<SortedSet<T>>() {
    override fun fromJson(reader: JsonReader): SortedSet<T> {
        val result: TreeSet<T> = TreeSet()
        reader.beginArray()
        while (reader.hasNext()) {
            elementAdapter.fromJson(reader)?.let { result.add(it) }
        }
        reader.endArray()
        return result
    }

    override fun toJson(writer: JsonWriter, value: SortedSet<T>?) {
        writer.beginArray()
        if(value != null) {
            for (element in value) {
                elementAdapter.toJson(writer, element)
            }
        }
        writer.endArray()
    }

}

class SortedSetAdapterFactory:JsonAdapter.Factory {
    override fun create(
        type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi
    ): JsonAdapter<*>? {
        if(annotations.isNotEmpty()) return null

        if(type !is ParameterizedType)return null

        if(type.rawType != SortedSet::class.java) return null

        val elementType = type.actualTypeArguments[0]
        val elementAdapter:JsonAdapter<Any?> = moshi.adapter(elementType)
        return SortedSetAdapter(elementAdapter).nullSafe()
    }

}

11、自定义代理 Delegate

fun customAdapterWithDelegate() {
    val data = """
  [
    {
        "name":"not-started"
    },
    {
        "name":"in-progress"
    },
    {
        "name":"in-progress2"
    },
    {
        "name":"rejected"
    },
    {
        "name":"completed"
    }
]
    """.trimIndent()

    val moshi = Moshi.Builder().add(StageAdapter()).add(KotlinJsonAdapterFactory()).build()
    val jsonAdapter = moshi.adapter<List<StageEle>>(
        Types.newParameterizedType(
            List::class.java,
            StageEle::class.java
        )
    )
    log(jsonAdapter.fromJson(data).toString())
}


class StageAdapter {
    @FromJson
    fun fromJson(jsonReader: JsonReader, delegate: JsonAdapter<Stage>): Stage {
        val value = jsonReader.nextString()

        return if (value.startsWith("in-progress")) {
            Stage.IN_PROGRESS
        } else {
            delegate.fromJsonValue(value) ?: Stage.IN_PROGRESS
        }
    }
}

enum class Stage {
    @Json(name = "not-started") NOT_STARTED, @Json(name = "in-progress") IN_PROGRESS,
    @Json(name = "rejected") REJECTED, @Json(name = "completed") COMPLETED
}

data class StageEle(val name: Stage)

12、JsonQualifier 颜色转换

class ColorAdapter {

    @ToJson
    fun toJson(@HexColor rgb: Int): String = String.format("#%06x", rgb)

    @FromJson
    @HexColor
    fun fromJson(rgb: String): Int = Integer.parseInt(rgb.substring(1), 16)
}

@Retention(AnnotationRetention.RUNTIME) @JsonQualifier annotation class HexColor

internal class Rectangle {
    private var hidth = 0
    private val height = 0

    @HexColor var color = 0override fun toString(): String {
        return String.format("%dx%d #%06x ->%d", width, height, color, color)
    }
}

fun customQualifier() {
    val data = """
        {
            "color":"#ff0000",
            "height":768,
            "width":1024
        }
    """.trimIndent()

    val moshi = Moshi.Builder().add(ColorAdapter()).add(KotlinJsonAdapterFactory()).build()
    val jsonAdapter = moshi.adapter(
        Rectangle::class.java
    )

    val rectangle: Rectangle = jsonAdapter.fromJson(data)
    log(rectangle.toString())
}

12、跳过错误值,并把错误值设置为默认值

class DefaultOnDataMismatchAdapter<T> private constructor(private val delegate: JsonAdapter<T>, private val defaultValue: T) :
    JsonAdapter<T>() {
    override fun fromJson(reader: JsonReader): T? {
        val peeked = reader.peekJson()
        return try {
            delegate.fromJson(peeked)
        } catch (e: Exception) {
            defaultValue
        } finally {
            peeked.close()

            // Skip the value back on the reader, no matter the state of the peeked reader.
            reader.skipValue()
        }
    }

    override fun toJson(writer: JsonWriter, value: T?) {
        delegate.toJson(writer, value)
    }

    companion object{
        fun <T>newFactory(type:Class<T>,defaultValue: T): Factory =object :Factory{
            override fun create(
                requestedType: Type, annotations: MutableSet<out Annotation>, moshi: Moshi
            ): JsonAdapter<T>? {
                if (type != requestedType) return null
                val delegate: JsonAdapter<T> = moshi.nextAdapter(this, type, annotations)
                return DefaultOnDataMismatchAdapter(delegate, defaultValue)
            }

        }
    }
}

fun recoverFromTypeMismatch() {
    // 只接收 CLUBS(梅花) ,DIAMONDS(方块),HEARTS(红桃), SPADES(黑桃) 所以 STARS 自动转化为 Suit.CLUBS
    val json = """
        ["DIAMONDS", "STARS", "HEARTS"]
    """.trimIndent()

    val moshi =
        Moshi.Builder().add(newFactory(Suit::class.java, Suit.CLUBS))
            .build()
    val jsonAdapter: JsonAdapter<List<Suit>> = moshi.adapter(
        Types.newParameterizedType(
            MutableList::class.java, Suit::class.java
        )
    )

    log(jsonAdapter.fromJson(json).toString())
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容