实现可观察到的性质,也可以列化在科特林

0

的问题

我在试图建立一个类,其中某些值可观察到的但也序列化。

这显然作品和化工作,但它的样板非常沉重的具有添加一定者为每一个领域,并手动具有叫 change(...) 在每个器:

interface Observable {

    fun change(message: String) {
        println("changing $message")
    }
}

@Serializable
class BlahVO : Observable {

    var value2: String = ""
        set(value) {
            field = value
            change("value2")
        }

    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

println(BlahVO().apply { value2 = "test2" }) 正确的产出

changing value2
{"value2":"test2"}

我已经试过介绍各位代表:

interface Observable {

    fun change(message: String) {
        println("changing $message")
    }

    
    @Suppress("ClassName")
    class default<T>(defaultValue: T) {

        private var value: T = defaultValue

        operator fun getValue(observable: Observable, property: KProperty<*>): T {
            return value
        }

        operator fun setValue(observable: Observable, property: KProperty<*>, value: T) {
            this.value = value
            observable.change(property.name)
        }

    }

}

@Serializable
class BlahVO : Observable {

    var value1: String by Observable.default("value1")

    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

println(BlahVO().apply { value1 = "test1" }) 正确地触发的变化探测的,但它不serialize:

changing value1
{}

如果我从中可观察到ReadWriteProperty,

interface Observable {

    fun change(message: String) {
        println("changing $message")
    }

    fun <T> look(defaultValue: T): ReadWriteProperty<Observable, T> {
        return OP(defaultValue, this)
    }

    class OP<T>(defaultValue: T, val observable: Observable) : ObservableProperty<T>(defaultValue) {
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            super.setValue(thisRef, property, value)
            observable.change("blah!")
        }
    }
}

@Serializable
class BlahVO : Observable {

    var value3: String by this.look("value3")

    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

结果是相同的:

changing blah!
{}

同样,对于代表。可否决

var value4: String by Delegates.vetoable("value4", {
        property: KProperty<*>, oldstring: String, newString: String ->
    this.change(property.name)
    true
})

产出:

changing value4
{}

各位代表似乎并不与科特林化

什么其他选择都是没有观察到一个酒店的改变,而不打破其化,也将在其他平台(KotlinJS,KotlinJVM,机器人,...)?

1

最好的答案

2

序列化和反的科特林代表团不支持 kotlinx.serialization 作为现在。
有一个 开放的问题#1578 在审查有关这个功能。

根据这个问题你可以创建一个中间数据-传送对象,其变化,而不是原始的对象。 你也可以写一个自定义的程序,以支持化的科特林代表,这似乎是更多的样板,然后编写定制吸气,并制定者提出的问题。


数据传送对象

通过映射你原来的目的一个简单的数据传送对象,而不代表你可以利用的默认化机制。 这也有很好的副作用来净化你的数据模型类框架具体的注释,例如 @Serializable.

class DataModel {
    var observedProperty: String by Delegates.observable("initial") { property, before, after ->
        println("""Hey, I changed "${property.name}" from "$before" to "$after"!""")
    }

    fun toJson(): String {
        return Json.encodeToString(serializer(), this.toDto())
    }
}

fun DataModel.toDto() = DataTransferObject(observedProperty)

@Serializable
class DataTransferObject(val observedProperty: String)

fun main() {
    val data = DataModel()
    println(data.toJson())
    data.observedProperty = "changed"
    println(data.toJson())
}

这将产生以下结果:

{"observedProperty":"initial"}
Hey, I changed "observedProperty" from "initial" to "changed"!
{"observedProperty":"changed"}

定义数据的类型

如果改变的数据类型是一个选项,可以编写一个包装类,获取(de)化的透明度。 沿线的东西以下可能的工作。

@Serializable
class ClassWithMonitoredString(val monitoredProperty: MonitoredString) {
    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

fun main() {
    val monitoredString = obs("obsDefault") { before, after ->
        println("""I changed from "$before" to "$after"!""")
    }
    
    val data = ClassWithMonitoredString(monitoredString)
    println(data.toJson())
    data.monitoredProperty.value = "obsChanged"
    println(data.toJson())
}

这将产生以下结果:

{"monitoredProperty":"obsDefault"}
I changed from "obsDefault" to "obsChanged"!
{"monitoredProperty":"obsChanged"}

你但是失去的信息有关其财产改变了,因为你没有容易进入的领域的名称。 你也要改变你的数据结构,如上所述,可能不希望的或者甚至是可能的。 此外,这项工作仅用于串现在,即使一个可能会使它更加普通。 此外,这需要大量的样板的开始。 在电话网站但是,你只有裹的实际价值,在一个呼叫 obs. 我用下面的样板得到它的工作。

typealias OnChange = (before: String, after: String) -> Unit

@Serializable(with = MonitoredStringSerializer::class)
class MonitoredString(initialValue: String, var onChange: OnChange?) {
    var value: String = initialValue
        set(value) {
            onChange?.invoke(field, value)

            field = value
        }

}

fun obs(value: String, onChange: OnChange? = null) = MonitoredString(value, onChange)

object MonitoredStringSerializer : KSerializer<MonitoredString> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MonitoredString", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: MonitoredString) {
        encoder.encodeString(value.value)
    }

    override fun deserialize(decoder: Decoder): MonitoredString {
        return MonitoredString(decoder.decodeString(), null)
    }
}
2021-11-24 18:19:41

我目前按照类似的方法,但它感觉它可能会更好。 我已经走了一步的步骤创建一个方法monitoredString其返回MonitoredString和由于职能具有接到这个,我没有过的菜单选择变化,我可以链接到菜单选择变化。 下行的具有观察到"国家"一类,然后一个数据传输类,可以化是重复的模型领域。 似乎唯一的好的解决方案实现了什么我想要做的,就是以注释与@的东西然后生成的样板,使用带.
Jan Vladimir Mostert

其他语言

此页面有其他语言版本

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................