1616 * Converts between {@link Object} properties and byte arrays using FlexBuffers.
1717 * <p>
1818 * Types are limited to those supported by FlexBuffers, including that map keys must be {@link String}.
19+ * (There are subclasses available that auto-convert {@link Integer} and {@link Long} key maps,
20+ * see {@link #convertToKey}.)
1921 * <p>
20- * Regardless of the stored type, integers are restored as {@link Long} if the value does not fit {@link Integer},
21- * otherwise as {@link Integer}. So e.g. when storing a {@link Long} value of {@code 1L}, the value restored from the
22+ * If any item requires 64 bits for storage in the FlexBuffers Map/Vector (a large Long, a Double)
23+ * all integers are restored as {@link Long}, otherwise {@link Integer}.
24+ * So e.g. when storing only a {@link Long} value of {@code 1L}, the value restored from the
2225 * database will be of type {@link Integer}.
26+ * (There are subclasses available that always restore as {@link Long}, see {@link #shouldRestoreAsLong}.)
2327 * <p>
2428 * Values of type {@link Float} are always restored as {@link Double}.
2529 * Cast to {@link Float} to obtain the original value.
@@ -85,6 +89,15 @@ private void addValue(FlexBuffersBuilder builder, Object value) {
8589 }
8690 }
8791
92+ /**
93+ * Checks Java map key is of the expected type, otherwise throws.
94+ */
95+ protected void checkMapKeyType (Object rawKey ) {
96+ if (!(rawKey instanceof String )) {
97+ throw new IllegalArgumentException ("Map keys must be String" );
98+ }
99+ }
100+
88101 private void addMap (FlexBuffersBuilder builder , String mapKey , Map <Object , Object > map ) {
89102 int mapStart = builder .startMap ();
90103
@@ -94,9 +107,7 @@ private void addMap(FlexBuffersBuilder builder, String mapKey, Map<Object, Objec
94107 if (rawKey == null || value == null ) {
95108 throw new IllegalArgumentException ("Map keys or values must not be null" );
96109 }
97- if (!(rawKey instanceof String )) {
98- throw new IllegalArgumentException ("Map keys must be String" );
99- }
110+ checkMapKeyType (rawKey );
100111 String key = rawKey .toString ();
101112 if (value instanceof Map ) {
102113 //noinspection unchecked
@@ -189,13 +200,22 @@ public Object convertToEntityProperty(byte[] databaseValue) {
189200 }
190201 }
191202
203+ /**
204+ * Converts a FlexBuffers string map key to the Java map key (e.g. String to Integer).
205+ * <p>
206+ * This required conversion restricts all keys (root and embedded maps) to the same type.
207+ */
208+ Object convertToKey (String keyValue ) {
209+ return keyValue ;
210+ }
211+
192212 /**
193213 * Returns true if the width in bytes stored in the private parentWidth field of FlexBuffers.Reference is 8.
194214 * Note: FlexBuffers stores all items in a map/vector using the size of the widest item. However,
195215 * an item's size is only as wide as needed, e.g. a 64-bit integer (Java Long, 8 bytes) will be
196216 * reduced to 1 byte if it does not exceed its value range.
197217 */
198- private boolean shouldRestoreAsLong (FlexBuffers .Reference reference ) {
218+ protected boolean shouldRestoreAsLong (FlexBuffers .Reference reference ) {
199219 try {
200220 Field parentWidthF = reference .getClass ().getDeclaredField ("parentWidth" );
201221 parentWidthF .setAccessible (true );
@@ -217,7 +237,8 @@ private Map<Object, Object> buildMap(FlexBuffers.Map map) {
217237 // So set initial capacity based on default load factor 0.75 accordingly.
218238 Map <Object , Object > resultMap = new HashMap <>((int ) (entryCount / 0.75 + 1 ));
219239 for (int i = 0 ; i < entryCount ; i ++) {
220- String key = keys .get (i ).toString ();
240+ String rawKey = keys .get (i ).toString ();
241+ Object key = convertToKey (rawKey );
221242 FlexBuffers .Reference value = values .get (i );
222243 if (value .isMap ()) {
223244 resultMap .put (key , buildMap (value .asMap ()));
0 commit comments