Gson 或 Moshi:POJO 中的字段可以有 2 种类型,如何保存到任何字段
Gson or Moshi: field in POJO could have 2 types, how to save to any of the fields
已编辑:
这是我拥有的 json 字符串:
json#1
{
[
{
field1 : ""
field2 : 0
field3 : "Amount not fixed" or field : 250 // this field can be string or int
},
{
field1 : ""
field2 : 0
field3 : "Amount not fixed" or field : 250 // this field can be string or int
}
]
}
json#2
{
field1 : ""
field2 : 0
field3 : "Amount not fixed" or field : 250 // this field can be string or int
}
或者它可以是来自服务器的任何 json 字符串。这里的重点是可能有 1 个或多个字段可能具有动态值(在这种情况下,field3 可以是字符串或 int)
然后我想将它们反序列化为任何 POJO
class Temp1 {
// field1 here
// field2 here
@SerializedName("field3")
val field3Int: Int? = null
@SerializedName("field3")
val field3String: String? = null
}
表示如果从服务器发送的值是Int
,我想将值设置为field3Int
。如果是String
,设置为field3String
。
可能还有其他 POJO 具有此类可能具有动态值的字段。
感谢 Serj 的回答,但在我编辑问题以显示我的真实情况后,我仍然无法在 TypeAdapter class 上工作。
顺便说一句。我像这样将它与 Retrofit2 一起使用:
val moshi = Moshi.Builder()
.add(MultitypeJsonAdapterAdapter())
.build()
return Retrofit.Builder().baseUrl(baseUrl)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.client(httpClient.build())
.build()
通过 Moshi
,您可以利用多态反序列化功能。只需编写一个将使用 JsonReader#readJsonValue()
的自定义适配器。请参阅下面的代码:
data class Multitype constructor(val fieldInt: Int?, val fieldString: String?) {
constructor(fieldInt: Int) : this(fieldInt, null)
constructor(fieldString: String) : this(null, fieldString)
}
class MultitypeJsonAdapterAdapter {
@FromJson fun fromJson(reader: JsonReader): Multitype {
val jsonValue = reader.readJsonValue() as Map<String, Any?>
val field = jsonValue["field"]
return when (field) {
is String -> Multitype(field)
is Double -> Multitype(field.toInt()) // readJsonValue parses numbers as Double
else -> throw JsonDataException("Expected a field of type Int or String")
}
}
@ToJson fun toJson(writer: JsonWriter, value: Multitype?) {
TODO("not implemented")
}
}
class MultitypeJsonAdapterAdapterTest {
@Test fun check() {
val moshi = Moshi.Builder()
.add(MultitypeJsonAdapterAdapter())
.add(KotlinJsonAdapterFactory())
.build()
val adapter = moshi.adapter(Multitype::class.java)
val fromJson1 = adapter.fromJson("""{ "field": 42 }""")
assertThat(fromJson1).isEqualTo(Multitype(42))
val fromJson2 = adapter.fromJson("""{ "field": "test" }""")
assertThat(fromJson2).isEqualTo(Multitype("test"))
}
}
我想我得到了我想要的。并且无需使用任何适配器。
如果字段可以具有任何动态类型,则需要在 POJO 中将其声明为 Any。然后如果你想使用它的实际值,你只需要检查它的类型并转换它。所以 POJO 应该是这样的:
class Temp1 {
// field1 here
// field2 here
@SerializedName("field3")
val field3: Any? = null
fun getField3Str() : String {
return when (field3) {
is String -> field3 as String
is Int -> {
"%d".format(field3 as Int)
}
is Double -> {
"%d".format(field3.toInt())
}
else -> ""
}
}
}
已编辑:
这是我拥有的 json 字符串:
json#1
{
[
{
field1 : ""
field2 : 0
field3 : "Amount not fixed" or field : 250 // this field can be string or int
},
{
field1 : ""
field2 : 0
field3 : "Amount not fixed" or field : 250 // this field can be string or int
}
]
}
json#2
{
field1 : ""
field2 : 0
field3 : "Amount not fixed" or field : 250 // this field can be string or int
}
或者它可以是来自服务器的任何 json 字符串。这里的重点是可能有 1 个或多个字段可能具有动态值(在这种情况下,field3 可以是字符串或 int)
然后我想将它们反序列化为任何 POJO
class Temp1 {
// field1 here
// field2 here
@SerializedName("field3")
val field3Int: Int? = null
@SerializedName("field3")
val field3String: String? = null
}
表示如果从服务器发送的值是Int
,我想将值设置为field3Int
。如果是String
,设置为field3String
。
可能还有其他 POJO 具有此类可能具有动态值的字段。
感谢 Serj 的回答,但在我编辑问题以显示我的真实情况后,我仍然无法在 TypeAdapter class 上工作。
顺便说一句。我像这样将它与 Retrofit2 一起使用:
val moshi = Moshi.Builder()
.add(MultitypeJsonAdapterAdapter())
.build()
return Retrofit.Builder().baseUrl(baseUrl)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.client(httpClient.build())
.build()
通过 Moshi
,您可以利用多态反序列化功能。只需编写一个将使用 JsonReader#readJsonValue()
的自定义适配器。请参阅下面的代码:
data class Multitype constructor(val fieldInt: Int?, val fieldString: String?) {
constructor(fieldInt: Int) : this(fieldInt, null)
constructor(fieldString: String) : this(null, fieldString)
}
class MultitypeJsonAdapterAdapter {
@FromJson fun fromJson(reader: JsonReader): Multitype {
val jsonValue = reader.readJsonValue() as Map<String, Any?>
val field = jsonValue["field"]
return when (field) {
is String -> Multitype(field)
is Double -> Multitype(field.toInt()) // readJsonValue parses numbers as Double
else -> throw JsonDataException("Expected a field of type Int or String")
}
}
@ToJson fun toJson(writer: JsonWriter, value: Multitype?) {
TODO("not implemented")
}
}
class MultitypeJsonAdapterAdapterTest {
@Test fun check() {
val moshi = Moshi.Builder()
.add(MultitypeJsonAdapterAdapter())
.add(KotlinJsonAdapterFactory())
.build()
val adapter = moshi.adapter(Multitype::class.java)
val fromJson1 = adapter.fromJson("""{ "field": 42 }""")
assertThat(fromJson1).isEqualTo(Multitype(42))
val fromJson2 = adapter.fromJson("""{ "field": "test" }""")
assertThat(fromJson2).isEqualTo(Multitype("test"))
}
}
我想我得到了我想要的。并且无需使用任何适配器。 如果字段可以具有任何动态类型,则需要在 POJO 中将其声明为 Any。然后如果你想使用它的实际值,你只需要检查它的类型并转换它。所以 POJO 应该是这样的:
class Temp1 {
// field1 here
// field2 here
@SerializedName("field3")
val field3: Any? = null
fun getField3Str() : String {
return when (field3) {
is String -> field3 as String
is Int -> {
"%d".format(field3 as Int)
}
is Double -> {
"%d".format(field3.toInt())
}
else -> ""
}
}
}