(de)serializing kotlin delegate properties with jackson

huangapple go评论72阅读模式
英文:

(de)serializing kotlin delegate properties with jackson

问题

如何使用Jackson序列化和反序列化Kotlin代理属性。
我有一个类似这样的类

class MyClass {
    var a: Int = 42
        set(value) {
			val changed = field != value
			field = value
			if (changed) notifyListeners()
		}
   
    ... 还有其他十几个遵循这种模式的属性 ...
}

我想通过以下方式简化它

class MyClass {
    var a: Int by NotifyUiOnChange(42)
    
    ...
    
    private inner class NotifyUiOnChange<T>(initialValue: T) : ObservableProperty<T>(initialValue) {
		override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
			notifyUiListeners()
		}
	}
}

但是Jackson会忽略该属性。
我该如何告诉Jackson序列化和反序列化该属性?
然后如何应用@JsonIgnore注释(或类似的内容)?

英文:

How can I (de)serialize kotlin delegate properties with jackson.
I have a class like this

class MyClass {
    var a: Int = 42
        set(value) {
			val changed = field != value
			field = value
			if (changed) notifyListeners()
		}
   
    ... and a dozen other properties that all follow this pattern ...
}

I wanted to simplify that by using

class MyClass {
    var a: Int by NotifyUiOnChange(42)
    
    ...
    
    private inner class NotifyUiOnChange&lt;T&gt;(initialValue: T) : ObservableProperty&lt;T&gt;(initialValue) {
		override fun afterChange(property: KProperty&lt;*&gt;, oldValue: T, newValue: T) {
			notifyUiListeners()
		}
	}
}

but then Jackson will ignore that property.
How can I tell Jackson to serialize and deserialize that property anyway?
And how do I then apply @JsonIgnore annotations (or something comparable)?

答案1

得分: 4

You must use outdated version on Jackson (or maybe a version for Java, not Kotlin?). I've checked this using "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.+" (resolved to 2.10.1).

I've declared two classes:

class MyClass {
    var a: Int = 42
        set(value) {
            val changed = field != value
            field = value
            if (changed) notifyListener(field)
        }

    private fun notifyListener(field: Any?) {
        println("changed: $field")
    }
}

class MyDelegatedClass {
    var a: Int by NotifyUi(42)

    private inner class NotifyUi<T>(initialValue: T) : ObservableProperty<T>(initialValue) {
        override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
            notifyListener(newValue)
        }
    }

    private fun notifyListener(field: Any?) {
        println("changed: $field")
    }
}

My main function:

fun main() {
    val noDelegate = MyClass()
    val delegated = MyDelegatedClass()

    val mapper = ObjectMapper().registerKotlinModule()

    // Deserialization 
    val noDelegateValue = mapper.writeValueAsString(noDelegate)
    val delegatedValue = mapper.writeValueAsString(delegated)

    println("No delegate:\t$noDelegateValue")
    println("With delegate\t$delegatedValue")

    // Serialization
    val noDelegateObject = mapper.readValue<MyClass>("{\"a\":42}".trimIndent())
    val delegateObject = mapper.readValue<MyDelegatedClass>("{\"a\":42}".trimIndent())
}

Output:

No delegate:	{"a":42}
With delegate	{"a":42}
changed: 42

We even can see output on delegate when we use delegate property (de)serializing kotlin delegate properties with jackson (I believe it's a side-effect that should be considered as a bug actually)

So, handling delegates is an out-of-the-box feature in Jackson (I am not sure since when, but I used lazy delegate with Jackson in an older project I used to participate and there were no problems with delegates).

How to ignore delegated property?

So, you cannot apply JsonIgnore annotation to delegated field because you will get This annotation is not applicable to target 'member property with delegate'. But, you can define the scope that the annotation should be applied. Example below:

class MyDelegateClass {
    @get:JsonIgnore // or set:
    val a: Int by NotifyUi(42)
}

Unfortunately, it seems that it's kind of broken because you can use get: or set: and it's not applied to the getter or setter only but for both.

英文:

You must use outdated version on Jackson (or maybe a version for Java, not Kotlin?). I've checked this using &quot;com.fasterxml.jackson.module:jackson-module-kotlin:2.10.+&quot; (resolved to 2.10.1).

I've declared two classes:

class MyClass {
    var a: Int = 42
        set(value) {
            val changed = field != value
            field = value
            if (changed) notifyListener(field)
        }

    private fun notifyListener(field: Any?) {
        println(&quot;changed: $field&quot;)
    }
}

class MyDelegatedClass {
    var a: Int by NotifyUi(42)

    private inner class NotifyUi&lt;T&gt;(initialValue: T) : ObservableProperty&lt;T&gt;(initialValue) {
        override fun afterChange(property: KProperty&lt;*&gt;, oldValue: T, newValue: T) {
            notifyListener(newValue)
        }
    }

    private fun notifyListener(field: Any?) {
        println(&quot;changed: $field&quot;)
    }
}

My main function:

fun main() {
    val noDelegate = MyClass()
    val delegated = MyDelegatedClass()

    val mapper = ObjectMapper().registerKotlinModule()

    // Deserialization 
    val noDelegateValue = mapper.writeValueAsString(noDelegate)
    val delegatedValue = mapper.writeValueAsString(delegated)

    println(&quot;No delegate:\t$noDelegateValue&quot;)
    println(&quot;With delegate\t$delegatedValue&quot;)

    // Serialization
    val noDelegateObject = mapper.readValue&lt;MyClass&gt;(&quot;{\&quot;a\&quot;:42}&quot;.trimIndent())
    val delegateObject = mapper.readValue&lt;MyDelegatedClass&gt;(&quot;{\&quot;a\&quot;:42}&quot;.trimIndent())

}

Output:

No delegate:	{&quot;a&quot;:42}
With delegate	{&quot;a&quot;:42}
changed: 42

We even can see output on delegate when we use delegate property (de)serializing kotlin delegate properties with jackson (I believe it's a side-effect that should be consider as bug actually)

So, handling delegates is out of the box feature in jackson (I am not sure since when, but I used lazy delegate with jackson in older project I used to participate and there was no problems with delegates).

How to ignore delegated property?

So, you cannot apply JsonIgnore annotation to delegated field, because you will get This annotation is not applicable to target &#39;member property with delegate&#39;. But, you can define the scope that annotation should be applied. Example below:

class MyDelegateClass {
    @get:JsonIgnore // or set:
    val a: Int by NotifyUi(42)
}

Unfortunately, seems that it's kind of broken, because you can use get: or set: and it's not apply to getter or setter only, but for both.

huangapple
  • 本文由 发表于 2020年1月3日 21:50:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/59579738.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定