如何在 Moshi 中将对象的字段编码为字符串 JSON 而不是嵌套的 JSON 对象?
How to encode a field of an object as stringifed JSON instead of nested JSON object in Moshi?
我有一个密封的 class WebSocketMessage
,其中有一些子class。 WebSocketMessage
有一个名为 type
的字段,用于区分子 class。
所有子classes 都有自己的名为payload
的字段,每个子class.
的类型不同
目前我正在使用 Moshi 的 PolymorphicJsonAdapterFactory
以便这些 classes 可以从 JSON 解析并编码为 JSON.
一切正常,但我需要的是将 payload
字段编码为字符串化 JSON 而不是 JSON 对象。
是否可以编写自定义适配器 class 来帮助我解决这个问题?或者是否有任何其他解决方案,以便我不必手动执行此字符串化?
我试过查看自定义适配器,但我找不到如何将 moshi 实例传递给适配器,以便我可以将给定字段编码为 JSON 然后将其字符串化,我也没有找到任何东西其他可以帮助我的。
WebSocketMessage
class 及其子class:
sealed class WebSocketMessage(
val type: Type
) {
enum class Type(val type: String) {
AUTH("AUTH"),
PING("PING"),
FLOW_INITIALIZATION("FLOW_INITIALIZATION")
}
class Ping : WebSocketMessage(Type.PING)
class InitFlow(payload: InitFlowMessage) : WebSocketMessage(Type.FLOW_INITIALIZATION)
class Auth(payload: Token) : WebSocketMessage(Type.AUTH)
}
PolymorphicJsonAdapterFactory
的 Moshi 实例:
val moshi = Moshi.Builder().add(
PolymorphicJsonAdapterFactory.of(WebSocketMessage::class.java, "type")
.withSubtype(WebSocketMessage.Ping::class.java, WebSocketMessage.Type.PING.type)
.withSubtype(
WebSocketMessage.InitFlow::class.java,
WebSocketMessage.Type.FLOW_INITIALIZATION.type
)
.withSubtype(WebSocketMessage.Auth::class.java, WebSocketMessage.Type.AUTH.type)
)
// Must be added last
.add(KotlinJsonAdapterFactory())
.build()
我如何编码为 JSON:
moshi.adapter(WebSocketMessage::class.java).toJson(WebSocketMessage.Auth(fetchToken()))
我目前得到 JSON 下一个格式:
{
"type":"AUTH",
"payload":{
"jwt":"some_token"
}
}
我想得到什么:
{
"type":"AUTH",
"payload":"{\"jwt\":\"some_token\"}"
}
在第二个示例中,有效载荷是一个字符串化的 JSON 对象,这正是我所需要的。
您可以创建自己的自定义 JsonAdapter
:
@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class AsString
/////////////////////
class AsStringAdapter<T>(
private val originAdapter: JsonAdapter<T>,
private val stringAdapter: JsonAdapter<String>
) : JsonAdapter<T>() {
companion object {
var FACTORY: JsonAdapter.Factory = object : Factory {
override fun create(
type: Type,
annotations: MutableSet<out Annotation>,
moshi: Moshi
): JsonAdapter<*>? {
val nextAnnotations = Types.nextAnnotations(annotations, AsString::class.java)
return if (nextAnnotations == null || !nextAnnotations.isEmpty())
null else {
AsStringAdapter(
moshi.nextAdapter<Any>(this, type, nextAnnotations),
moshi.nextAdapter<String>(this, String::class.java, Util.NO_ANNOTATIONS)
)
}
}
}
}
override fun toJson(writer: JsonWriter, value: T?) {
val jsonValue = originAdapter.toJsonValue(value)
val jsonStr = JSONObject(jsonValue as Map<*, *>).toString()
stringAdapter.toJson(writer, jsonStr)
}
override fun fromJson(reader: JsonReader): T? {
throw UnsupportedOperationException()
}
}
/////////////////////
class Auth(@AsString val payload: Token)
/////////////////////
.add(AsStringAdapter.FACTORY)
.add(KotlinJsonAdapterFactory())
.build()
我有一个密封的 class WebSocketMessage
,其中有一些子class。 WebSocketMessage
有一个名为 type
的字段,用于区分子 class。
所有子classes 都有自己的名为payload
的字段,每个子class.
目前我正在使用 Moshi 的 PolymorphicJsonAdapterFactory
以便这些 classes 可以从 JSON 解析并编码为 JSON.
一切正常,但我需要的是将 payload
字段编码为字符串化 JSON 而不是 JSON 对象。
是否可以编写自定义适配器 class 来帮助我解决这个问题?或者是否有任何其他解决方案,以便我不必手动执行此字符串化?
我试过查看自定义适配器,但我找不到如何将 moshi 实例传递给适配器,以便我可以将给定字段编码为 JSON 然后将其字符串化,我也没有找到任何东西其他可以帮助我的。
WebSocketMessage
class 及其子class:
sealed class WebSocketMessage(
val type: Type
) {
enum class Type(val type: String) {
AUTH("AUTH"),
PING("PING"),
FLOW_INITIALIZATION("FLOW_INITIALIZATION")
}
class Ping : WebSocketMessage(Type.PING)
class InitFlow(payload: InitFlowMessage) : WebSocketMessage(Type.FLOW_INITIALIZATION)
class Auth(payload: Token) : WebSocketMessage(Type.AUTH)
}
PolymorphicJsonAdapterFactory
的 Moshi 实例:
val moshi = Moshi.Builder().add(
PolymorphicJsonAdapterFactory.of(WebSocketMessage::class.java, "type")
.withSubtype(WebSocketMessage.Ping::class.java, WebSocketMessage.Type.PING.type)
.withSubtype(
WebSocketMessage.InitFlow::class.java,
WebSocketMessage.Type.FLOW_INITIALIZATION.type
)
.withSubtype(WebSocketMessage.Auth::class.java, WebSocketMessage.Type.AUTH.type)
)
// Must be added last
.add(KotlinJsonAdapterFactory())
.build()
我如何编码为 JSON:
moshi.adapter(WebSocketMessage::class.java).toJson(WebSocketMessage.Auth(fetchToken()))
我目前得到 JSON 下一个格式:
{
"type":"AUTH",
"payload":{
"jwt":"some_token"
}
}
我想得到什么:
{
"type":"AUTH",
"payload":"{\"jwt\":\"some_token\"}"
}
在第二个示例中,有效载荷是一个字符串化的 JSON 对象,这正是我所需要的。
您可以创建自己的自定义 JsonAdapter
:
@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class AsString
/////////////////////
class AsStringAdapter<T>(
private val originAdapter: JsonAdapter<T>,
private val stringAdapter: JsonAdapter<String>
) : JsonAdapter<T>() {
companion object {
var FACTORY: JsonAdapter.Factory = object : Factory {
override fun create(
type: Type,
annotations: MutableSet<out Annotation>,
moshi: Moshi
): JsonAdapter<*>? {
val nextAnnotations = Types.nextAnnotations(annotations, AsString::class.java)
return if (nextAnnotations == null || !nextAnnotations.isEmpty())
null else {
AsStringAdapter(
moshi.nextAdapter<Any>(this, type, nextAnnotations),
moshi.nextAdapter<String>(this, String::class.java, Util.NO_ANNOTATIONS)
)
}
}
}
}
override fun toJson(writer: JsonWriter, value: T?) {
val jsonValue = originAdapter.toJsonValue(value)
val jsonStr = JSONObject(jsonValue as Map<*, *>).toString()
stringAdapter.toJson(writer, jsonStr)
}
override fun fromJson(reader: JsonReader): T? {
throw UnsupportedOperationException()
}
}
/////////////////////
class Auth(@AsString val payload: Token)
/////////////////////
.add(AsStringAdapter.FACTORY)
.add(KotlinJsonAdapterFactory())
.build()