From 9302cc54574439df3ece787efc4b3b9d3fd056cc Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 17 May 2019 13:48:46 +0200 Subject: [PATCH] fix case "skipped empty list and not null parameter" GitOrigin-RevId: 42e81739dee2a0585c0b52825c7c4bfab3ef32e9 --- .../src/CollectionBinding.kt | 32 ++++++++++++++++++- platform/object-serializer/src/MapBinding.kt | 7 +++- .../object-serializer/src/ObjectSerializer.kt | 28 ++++++++++------ .../several recursive reference.ion | 8 +++-- .../testSrc/NonDefaultConstructorTest.kt | 7 +++- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/platform/object-serializer/src/CollectionBinding.kt b/platform/object-serializer/src/CollectionBinding.kt index 1a0e14b6ad33..4ccc6848039a 100644 --- a/platform/object-serializer/src/CollectionBinding.kt +++ b/platform/object-serializer/src/CollectionBinding.kt @@ -45,6 +45,11 @@ internal class CollectionBinding(type: ParameterizedType, context: BindingInitia private val collectionClass = ClassUtil.typeToClass(type) override fun deserialize(context: ReadContext): Collection { + if (context.reader.type == IonType.INT) { + LOG.assertTrue(context.reader.intValue() == 0) + return emptyList() + } + val result = createCollection() readInto(result, context) return result @@ -53,6 +58,12 @@ internal class CollectionBinding(type: ParameterizedType, context: BindingInitia override fun serialize(obj: Any, context: WriteContext) { val writer = context.writer val collection = obj as Collection<*> + if (context.filter.skipEmptyCollection && collection.isEmpty()) { + // some value must be written otherwise on deserialize null will be used for constructor parameters (and it can be not expected) + writer.writeInt(0) + return + } + writer.stepIn(IonType.LIST) collection.forEach(createItemConsumer(context)) writer.stepOut() @@ -64,6 +75,9 @@ internal class CollectionBinding(type: ParameterizedType, context: BindingInitia property.set(hostObject, null) return } + else if (type == IonType.INT /* empty collection if context.filter.skipEmptyCollection */) { + return + } @Suppress("UNCHECKED_CAST") var result = property.readUnsafe(hostObject) as MutableCollection? @@ -105,11 +119,27 @@ internal class CollectionBinding(type: ParameterizedType, context: BindingInitia internal class ArrayBinding(private val itemClass: Class<*>, context: BindingInitializationContext) : BaseCollectionBinding(itemClass, context) { override fun deserialize(context: ReadContext) = readArray(context) + override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { + val type = context.reader.type + if (type == IonType.NULL) { + property.set(hostObject, null) + } + else if (type != IonType.INT) { + property.set(hostObject, readArray(context)) + } + } + override fun serialize(obj: Any, context: WriteContext) { + val array = obj as Array<*> val writer = context.writer + if (context.filter.skipEmptyArray && array.isEmpty()) { + writer.writeInt(0) + return + } + writer.stepIn(IonType.LIST) val consumer = createItemConsumer(context) - (obj as Array<*>).forEach { consumer.accept(it) } + array.forEach { consumer.accept(it) } writer.stepOut() } diff --git a/platform/object-serializer/src/MapBinding.kt b/platform/object-serializer/src/MapBinding.kt index e6cecbabf165..f0ad2636a978 100644 --- a/platform/object-serializer/src/MapBinding.kt +++ b/platform/object-serializer/src/MapBinding.kt @@ -14,8 +14,13 @@ internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitia private val isKeyComparable = Comparable::class.java.isAssignableFrom(ClassUtil.typeToClass(keyType)) override fun serialize(obj: Any, context: WriteContext) { - val writer = context.writer val map = obj as Map<*, *> + val writer = context.writer + + if (context.filter.skipEmptyMap && map.isEmpty()) { + writer.writeInt(0) + return + } fun writeEntry(key: Any?, value: Any?) { if (key == null) { diff --git a/platform/object-serializer/src/ObjectSerializer.kt b/platform/object-serializer/src/ObjectSerializer.kt index d43d4a8d3540..56cec93a9848 100644 --- a/platform/object-serializer/src/ObjectSerializer.kt +++ b/platform/object-serializer/src/ObjectSerializer.kt @@ -85,19 +85,29 @@ class ObjectSerializer { } interface SerializationFilter { + val skipEmptyCollection: Boolean + get() = false + + val skipEmptyMap: Boolean + get() = false + + val skipEmptyArray: Boolean + get() = false + fun isSkipped(value: Any?): Boolean } object SkipNullAndEmptySerializationFilter : SerializationFilter { - override fun isSkipped(value: Any?): Boolean { - return when (value) { - null -> true - is Collection<*> -> value.isEmpty() - is Array<*> -> value.isEmpty() - is Map<*, *> -> value.isEmpty() - else -> false - } - } + override fun isSkipped(value: Any?) = value == null + + override val skipEmptyCollection: Boolean + get() = true + + override val skipEmptyMap: Boolean + get() = true + + override val skipEmptyArray: Boolean + get() = true } class ObjectIdWriter { diff --git a/platform/object-serializer/testSnapshots/several recursive reference.ion b/platform/object-serializer/testSnapshots/several recursive reference.ion index f914117c5ad1..dd9639111196 100644 --- a/platform/object-serializer/testSnapshots/several recursive reference.ion +++ b/platform/object-serializer/testSnapshots/several recursive reference.ion @@ -2,6 +2,10 @@ '@id':0, bean:{ '@id':1, - bean:0 - } + bean:0, + list:0, + children:0 + }, + list:0, + children:0 } \ No newline at end of file diff --git a/platform/object-serializer/testSrc/NonDefaultConstructorTest.kt b/platform/object-serializer/testSrc/NonDefaultConstructorTest.kt index 1f3526449088..919d0ea04837 100644 --- a/platform/object-serializer/testSrc/NonDefaultConstructorTest.kt +++ b/platform/object-serializer/testSrc/NonDefaultConstructorTest.kt @@ -19,13 +19,18 @@ class NonDefaultConstructorTest { @Rule val fsRule = InMemoryFsRule() - private fun test(bean: Any) = test(bean, testName, defaultTestWriteConfiguration) + private fun test(bean: Any, writeConfiguration: WriteConfiguration = defaultTestWriteConfiguration) = test(bean, testName, writeConfiguration) @Test fun `no default constructor`() { test(NoDefaultConstructorBean("foo", arrayListOf(42, 21))) } + @Test + fun `skipped empty list and not null parameter`() { + test(NoDefaultConstructorBean("foo", emptyList()), defaultTestWriteConfiguration.copy(filter = SkipNullAndEmptySerializationFilter)) + } + @Test fun `null string as arg`() { test(NullableArgBean("foo", null, ""))