Java 使Gson为外场确定的接口类型工作
问题类似于 但不同之处在于,Java 使Gson为外场确定的接口类型工作,java,android,kotlin,gson,deserialization,Java,Android,Kotlin,Gson,Deserialization,问题类似于 但不同之处在于,类型不包含在反序列化的JSON对象中,而是包含在外部级别(根)中 像这样: { "type": "A", "myObject" : { <- type is NOT stored in here ... } } 如果type是A和Baz如果type是B,如何将myObject映射到Bar 暂时,我求助于手动读取根JSON对象。任何帮助都将不胜感激。谢谢 编辑: 当尝试使用GsonfromJson方法将根JSON对象映射到
类型
不包含在反序列化的JSON对象中,而是包含在外部级别(根)中
像这样:
{
"type": "A",
"myObject" : { <- type is NOT stored in here
...
}
}
如果type
是A
和Baz
如果type
是B
,如何将myObject
映射到Bar
暂时,我求助于手动读取根JSON对象。任何帮助都将不胜感激。谢谢
编辑:
当尝试使用
Gson
fromJson
方法将根JSON对象映射到Blah
时,我遇到以下错误:无法为类Foo调用无参数构造函数
-但无论如何,这是不正确的,因为我需要myObject
专门映射到Baz
或Bar
这与您提到的问题非常相似
您可以为Blah
定义反序列化器,并决定应使用哪个类
代码如下所示
import com.google.gson.*
interface Foo {
fun bark()
}
class Bar : Foo { // Map to this if 'type' is A
override fun bark() {
print("bar")
}
}
class Baz : Foo { // Map to this if 'type' is B
override fun bark() {
print("baz")
}
}
class Blah(val type : String? = null, val myObject : Foo? = null) {
companion object {
const val TYPE_A = "A"
const val TYPE_B = "B"
}
}
class BlahJsonDeserializer: JsonDeserializer<Blah> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Blah {
val root = json?.asJsonObject
val type = root?.get("type")?.asString
var obj: Foo? = null
when(type ?: "") {
Blah.TYPE_A -> { obj = Bar() }
Blah.TYPE_B -> { obj = Baz() }
}
val blah = Blah(type, obj)
return blah
}
}
val json = "{'type': 'A', 'myObject': {}}"
val gsonBuilder = GsonBuilder()
gsonBuilder.registerTypeAdapter(Blah::class.java, BlahJsonDeserializer())
val gson = gsonBuilder.create()
val item = gson.fromJson<Blah>(json, Blah::class.java)
item.myObject?.bark() // bar
导入com.google.gson*
接口Foo{
风趣的树皮
}
类栏:Foo{//如果“type”是
覆盖有趣的树皮(){
打印(“条”)
}
}
类Baz:Foo{//如果“type”是B,则映射到此
覆盖有趣的树皮(){
打印(“baz”)
}
}
类Blah(val类型:String?=null,val myObject:Foo?=null){
伴星{
const val TYPE_A=“A”
const val TYPE_B=“B”
}
}
类BlahJsonDeserializer:JsonDeserializer{
重写有趣的反序列化(json:JsonElement?、typeOfT:Type?、context:JsonDeserializationContext?):Blah{
val root=json?.asJsonObject
val type=root?.get(“type”)?.asString
变量obj:Foo?=null
何时(键入?:“”){
Blah.TYPE_A->{obj=Bar()}
Blah.TYPE_B->{obj=Baz()}
}
val blah=blah(类型,obj)
回音
}
}
val json=“{'type':'A','myObject':{}”
val gsonBuilder=gsonBuilder()
gsonBuilder.registerTypeAdapter(Blah::class.java,BlahJsonDeserializer())
val gson=gsonBuilder.create()
val item=gson.fromJson(json,Blah::class.java)
item.myObject?.bark()//条
这是我的解决方案。解决方案在科特林 编辑类
Blah
:
class Blah {
val type : String? = null
@ExcludeOnDeserialization // <- add this
val myObject : Foo? = null
}
这个解决方案的灵感来自谢谢,但我避免了这样做,因为这意味着我必须手动映射
Blah的所有字段,因为一个字段没有被映射。我在找一种半自动化的。我现在有一个解决方案,今天晚些时候会发布。我已经发布了我的答案。再次感谢。
class Blah {
val type : String? = null
@ExcludeOnDeserialization // <- add this
val myObject : Foo? = null
}
inline fun <T : Annotation>findAnnotatedFields(
annotation: Class<T>,
clazz : Class<*>,
onFind : (Field) -> Unit
){
for(field in clazz.declaredFields){
if(field.getAnnotation(annotation)!=null){
field.isAccessible = true
onFind(field)
}
}
}
@Target(AnnotationTarget.FIELD)
annotation class ExcludeOnDeserialization
class GsonExclusionStrategy : ExclusionStrategy {
override fun shouldSkipClass(clazz: Class<*>?): Boolean {
return clazz?.getAnnotation(ExcludeOnDeserialization::class.java) != null
}
override fun shouldSkipField(f: FieldAttributes?): Boolean {
return f?.getAnnotation(ExcludeOnDeserialization::class.java) != null
}
}
...
val gson = GsonBuilder()
.addDeserializationExclusionStrategy(GsonExclusionStrategy())
.create()
val rootJsonObject = JsonParser().parse(rootJsonObjectAsString)
val blah = gson.fromJson(rootJsonObject, Blah::class.java)
findAnnotatedFields(
ExcludeOnDeserialization::class.java,
Blah::class.java
){ foundExcludedField -> // foundExcludedField = 'myObject' declared in 'Blah' class
val myObjectAsJsonObject
= rootJsonObject.asJsonObject.getAsJsonObject(foundExcludedField.name)
when (foundExcludedField.type) {
Foo::class.java -> {
when (blah.type) {
"A" -> {
foundExcludedField.set(
blah,
gson.fromJson(myObjectAsJsonObject, Bar::class.java)
)
}
"B" -> {
foundExcludedField.set(
blah,
gson.fromJson(myObjectAsJsonObject, Baz::class.java)
)
}
else -> return null
}
}
}
}
// The root json object has now fully been mapped into 'blah'