diff --git a/src/main/java/com/surrealdb/ValueBuilder.java b/src/main/java/com/surrealdb/ValueBuilder.java index bd68f9ff..0759bc38 100644 --- a/src/main/java/com/surrealdb/ValueBuilder.java +++ b/src/main/java/com/surrealdb/ValueBuilder.java @@ -82,6 +82,14 @@ private static ValueMut convertObject(final T object) throws IllegalAccessEx if (object instanceof RecordId) { return ValueMut.createThing((RecordId) object); } + if (object instanceof byte[]) { + final byte[] bytes = (byte[]) object; + final List values = new ArrayList<>(bytes.length); + for (final byte b : bytes) { + values.add(ValueMut.createLong(b & 0xFF)); + } + return ValueMut.createArray(values); + } if (object instanceof Array) { return ValueMut.createArray((Array) object); } diff --git a/src/main/java/com/surrealdb/ValueClassConverter.java b/src/main/java/com/surrealdb/ValueClassConverter.java index 597adea7..05b7e6cd 100644 --- a/src/main/java/com/surrealdb/ValueClassConverter.java +++ b/src/main/java/com/surrealdb/ValueClassConverter.java @@ -166,6 +166,19 @@ private static T convert(Class clazz, Object source) throws ReflectiveOpe private static void setFieldObject(Field field, Class type, T target, V value) throws ReflectiveOperationException { if (Optional.class.equals(type)) { field.set(target, Optional.of(value)); + } else if (type == byte[].class && value instanceof List) { + // Special handling for byte[] fields + final List list = (List) value; + final byte[] bytes = new byte[list.size()]; + for (int i = 0; i < list.size(); i++) { + final java.lang.Object element = list.get(i); + if (element instanceof Number) { + bytes[i] = ((Number) element).byteValue(); + } else { + throw new SurrealException("Cannot convert " + element.getClass() + " to byte"); + } + } + field.set(target, bytes); } else { field.set(target, value); } diff --git a/src/test/java/com/surrealdb/TypeTests.java b/src/test/java/com/surrealdb/TypeTests.java index 63bd95cc..23cca0e7 100644 --- a/src/test/java/com/surrealdb/TypeTests.java +++ b/src/test/java/com/surrealdb/TypeTests.java @@ -1,5 +1,6 @@ package com.surrealdb; +import com.surrealdb.pojos.ByteData; import com.surrealdb.pojos.Dates; import com.surrealdb.pojos.Name; import com.surrealdb.pojos.Numbers; @@ -10,6 +11,7 @@ import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.Iterator; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -80,4 +82,56 @@ void testRecordIds() { } } + @Test + void testByteArray() { + try (final Surreal surreal = new Surreal()) { + // Starts an embedded in memory instance + surreal.connect("memory").useNs("test_ns").useDb("test_db"); + + // Test 1: Create a new record with byte[] data from raw bytes + final ByteData byteData = new ByteData(); + byteData.data = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}; + // We ingest the record + final ByteData created = surreal.create(ByteData.class, "bytedata", byteData).get(0); + // We check that the records are matching + assertEquals(created, byteData); + + // Test 2: Create a new record with byte[] data converted from string + final ByteData byteDataFromString = new ByteData(); + final String testString = "Hello"; + byteDataFromString.data = testString.getBytes(); + // We ingest the record + final ByteData createdFromString = surreal.create(ByteData.class, "bytedata:string", byteDataFromString).get(0); + // We check that the records are matching + assertEquals(createdFromString, byteDataFromString); + // Verify we can convert the bytes back to string + final String retrievedString = new String(createdFromString.data); + assertEquals(retrievedString, testString); + + // Test 3: Select from database and verify byte[] is properly handled + final Iterator selectedRecords = surreal.select(ByteData.class, "bytedata"); + final ByteData selectedRecord = selectedRecords.next(); + // Compare the byte[] data content + assertEquals(selectedRecord.data.length, byteData.data.length); + for (int i = 0; i < selectedRecord.data.length; i++) { + assertEquals(selectedRecord.data[i], byteData.data[i]); + } + // Verify the byte array content is correct + assertEquals(selectedRecord.data.length, 5); + assertEquals(selectedRecord.data[0], 0x01); + assertEquals(selectedRecord.data[4], 0x05); + + // Test 4: Select the string-converted record and verify conversion back to string + final Iterator selectedStringRecords = surreal.select(ByteData.class, "bytedata:string"); + final ByteData selectedStringRecord = selectedStringRecords.next(); + // Compare the byte[] data content + assertEquals(selectedStringRecord.data.length, byteDataFromString.data.length); + for (int i = 0; i < selectedStringRecord.data.length; i++) { + assertEquals(selectedStringRecord.data[i], byteDataFromString.data[i]); + } + final String selectedRetrievedString = new String(selectedStringRecord.data); + assertEquals(selectedRetrievedString, testString); + } + } + } diff --git a/src/test/java/com/surrealdb/pojos/ByteData.java b/src/test/java/com/surrealdb/pojos/ByteData.java new file mode 100644 index 00000000..4cfd7af4 --- /dev/null +++ b/src/test/java/com/surrealdb/pojos/ByteData.java @@ -0,0 +1,23 @@ +package com.surrealdb.pojos; + +import java.util.Arrays; +import java.util.Objects; + +public class ByteData { + + public byte[] data; + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(data)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final ByteData byteData = (ByteData) o; + return Arrays.equals(data, byteData.data); + } +} +