Skip to content

Commit d0eae81

Browse files
Merge branch '131-flex-maps-support-null-entries' into 'dev'
Resolve "Flex maps: support null entries" See merge request objectbox/objectbox-java!141
2 parents 2f2f6ad + 1c7fc2b commit d0eae81

File tree

3 files changed

+66
-32
lines changed

3 files changed

+66
-32
lines changed

objectbox-java/src/main/java/io/objectbox/converter/FlexObjectConverter.java

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 ObjectBox Ltd. All rights reserved.
2+
* Copyright 2021-2024 ObjectBox Ltd. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,10 +16,6 @@
1616

1717
package io.objectbox.converter;
1818

19-
import io.objectbox.flatbuffers.ArrayReadWriteBuf;
20-
import io.objectbox.flatbuffers.FlexBuffers;
21-
import io.objectbox.flatbuffers.FlexBuffersBuilder;
22-
2319
import java.lang.reflect.Field;
2420
import java.nio.ByteBuffer;
2521
import java.util.ArrayList;
@@ -28,6 +24,10 @@
2824
import java.util.Map;
2925
import java.util.concurrent.atomic.AtomicReference;
3026

27+
import io.objectbox.flatbuffers.ArrayReadWriteBuf;
28+
import io.objectbox.flatbuffers.FlexBuffers;
29+
import io.objectbox.flatbuffers.FlexBuffersBuilder;
30+
3131
/**
3232
* Converts between {@link Object} properties and byte arrays using FlexBuffers.
3333
* <p>
@@ -126,12 +126,14 @@ private void addMap(FlexBuffersBuilder builder, String mapKey, Map<Object, Objec
126126
for (Map.Entry<Object, Object> entry : map.entrySet()) {
127127
Object rawKey = entry.getKey();
128128
Object value = entry.getValue();
129-
if (rawKey == null || value == null) {
130-
throw new IllegalArgumentException("Map keys or values must not be null");
129+
if (rawKey == null) {
130+
throw new IllegalArgumentException("Map keys must not be null");
131131
}
132132
checkMapKeyType(rawKey);
133133
String key = rawKey.toString();
134-
if (value instanceof Map) {
134+
if (value == null) {
135+
builder.putNull(key);
136+
} else if (value instanceof Map) {
135137
//noinspection unchecked
136138
addMap(builder, key, (Map<Object, Object>) value);
137139
} else if (value instanceof List) {
@@ -171,9 +173,8 @@ private void addVector(FlexBuffersBuilder builder, String vectorKey, List<Object
171173

172174
for (Object item : list) {
173175
if (item == null) {
174-
throw new IllegalArgumentException("List elements must not be null");
175-
}
176-
if (item instanceof Map) {
176+
builder.putNull();
177+
} else if (item instanceof Map) {
177178
//noinspection unchecked
178179
addMap(builder, null, (Map<Object, Object>) item);
179180
} else if (item instanceof List) {
@@ -213,7 +214,9 @@ public Object convertToEntityProperty(byte[] databaseValue) {
213214
if (databaseValue == null) return null;
214215

215216
FlexBuffers.Reference value = FlexBuffers.getRoot(new ArrayReadWriteBuf(databaseValue, databaseValue.length));
216-
if (value.isMap()) {
217+
if (value.isNull()) {
218+
return null;
219+
} else if (value.isMap()) {
217220
return buildMap(value.asMap());
218221
} else if (value.isVector()) {
219222
return buildList(value.asVector());
@@ -277,7 +280,9 @@ private Map<Object, Object> buildMap(FlexBuffers.Map map) {
277280
String rawKey = keys.get(i).toString();
278281
Object key = convertToKey(rawKey);
279282
FlexBuffers.Reference value = values.get(i);
280-
if (value.isMap()) {
283+
if (value.isNull()) {
284+
resultMap.put(key, null);
285+
} else if (value.isMap()) {
281286
resultMap.put(key, buildMap(value.asMap()));
282287
} else if (value.isVector()) {
283288
resultMap.put(key, buildList(value.asVector()));
@@ -314,7 +319,9 @@ private List<Object> buildList(FlexBuffers.Vector vector) {
314319

315320
for (int i = 0; i < itemCount; i++) {
316321
FlexBuffers.Reference item = vector.get(i);
317-
if (item.isMap()) {
322+
if (item.isNull()) {
323+
list.add(null);
324+
} else if (item.isMap()) {
318325
list.add(buildMap(item.asMap()));
319326
} else if (item.isVector()) {
320327
list.add(buildList(item.asVector()));

tests/objectbox-java-test/src/test/java/io/objectbox/converter/FlexMapConverterTest.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
1+
/*
2+
* Copyright 2020-2024 ObjectBox Ltd. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package io.objectbox.converter;
218

319
import org.junit.Test;
420

5-
import javax.annotation.Nullable;
621
import java.time.Instant;
722
import java.util.HashMap;
823
import java.util.LinkedList;
924
import java.util.List;
1025
import java.util.Map;
1126

27+
import javax.annotation.Nullable;
28+
29+
1230
import static org.junit.Assert.assertArrayEquals;
1331
import static org.junit.Assert.assertEquals;
1432
import static org.junit.Assert.assertThrows;
@@ -37,6 +55,7 @@ public void keysString_valsSupportedTypes_works() {
3755
map.put("long", 1L);
3856
map.put("float", 1.3f);
3957
map.put("double", -1.4d);
58+
map.put("null", null);
4059
Map<String, Object> restoredMap = convertAndBack(map, converter);
4160
// Java integers are returned as Long if one value is larger than 32 bits, so expect Long.
4261
map.put("byte", 1L);
@@ -158,10 +177,12 @@ public void nestedMap_works() {
158177
Map<String, String> embeddedMap1 = new HashMap<>();
159178
embeddedMap1.put("Hello1", "Grüezi1");
160179
embeddedMap1.put("💡1", "Idea1");
180+
embeddedMap1.put("null1", null);
161181
map.put("Hello", embeddedMap1);
162182
Map<String, String> embeddedMap2 = new HashMap<>();
163183
embeddedMap2.put("Hello2", "Grüezi2");
164184
embeddedMap2.put("💡2", "Idea2");
185+
embeddedMap2.put("null2", null);
165186
map.put("💡", embeddedMap2);
166187
convertAndBackThenAssert(map, converter);
167188
}
@@ -181,6 +202,7 @@ public void nestedList_works() {
181202
embeddedList1.add(-2L);
182203
embeddedList1.add(1.3f);
183204
embeddedList1.add(-1.4d);
205+
embeddedList1.add(null);
184206
map.put("Hello", embeddedList1);
185207
List<Object> embeddedList2 = new LinkedList<>();
186208
embeddedList2.add("Grüezi");
@@ -213,17 +235,12 @@ public void nestedListByteArray_works() {
213235
}
214236

215237
@Test
216-
public void nullKeyOrValue_throws() {
238+
public void nullKey_throws() {
217239
FlexObjectConverter converter = new StringFlexMapConverter();
218240
Map<String, String> map = new HashMap<>();
219241

220-
map.put("Hello", null);
221-
convertThenAssertThrows(map, converter, "Map keys or values must not be null");
222-
223-
map.clear();
224-
225242
map.put(null, "Idea");
226-
convertThenAssertThrows(map, converter, "Map keys or values must not be null");
243+
convertThenAssertThrows(map, converter, "Map keys must not be null");
227244
}
228245

229246
@Test

tests/objectbox-java-test/src/test/java/io/objectbox/converter/FlexObjectConverterTest.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
1+
/*
2+
* Copyright 2021-2024 ObjectBox Ltd. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package io.objectbox.converter;
218

319
import org.junit.Test;
420

5-
import javax.annotation.Nullable;
621
import java.util.LinkedList;
722
import java.util.List;
823

24+
import javax.annotation.Nullable;
25+
26+
927
import static org.junit.Assert.assertEquals;
10-
import static org.junit.Assert.assertThrows;
1128

1229
/**
1330
* Tests {@link FlexObjectConverter} basic types and flexible list conversion.
@@ -55,6 +72,7 @@ public void list_works() {
5572
list.add(-2L);
5673
list.add(1.3f);
5774
list.add(-1.4d);
75+
list.add(null);
5876
List<Object> restoredList = convertAndBack(list, converter);
5977
// Java integers are returned as Long as one element is larger than 32 bits, so expect Long.
6078
list.set(2, 1L);
@@ -63,14 +81,6 @@ public void list_works() {
6381
// Java Float is returned as Double, so expect Double.
6482
list.set(6, (double) 1.3f);
6583
assertEquals(list, restoredList);
66-
67-
// list with null element
68-
list.add(null);
69-
IllegalArgumentException exception = assertThrows(
70-
IllegalArgumentException.class,
71-
() -> convertAndBack(list, converter)
72-
);
73-
assertEquals("List elements must not be null", exception.getMessage());
7484
}
7585

7686
@SuppressWarnings("unchecked")

0 commit comments

Comments
 (0)