Skip to content

Type Conversion API

The type_conversion module provides utilities for converting between Python and Java types when working with ArcadeDB's Java backend.

Overview

The type_conversion module enables:

  • Automatic Conversion: Seamless Python ↔ Java type conversion
  • Collection Handling: Lists, sets, maps, and nested structures
  • Date/Time Support: datetime, date, and time objects
  • Decimal Precision: High-precision decimal numbers
  • Binary Data: Bytes and byte arrays
  • Type Safety: Validation and error handling

Why Type Conversion?

ArcadeDB Python bindings wrap a Java database engine. When you:

  • Set properties on records → Python values converted to Java
  • Read properties from records → Java values converted to Python
  • Pass query parameters → Python values converted to Java
  • Receive query results → Java values converted to Python

The type_conversion module handles this automatically.

Conversion Functions

convert_python_to_java

from arcadedb_embedded.type_conversion import convert_python_to_java

java_value = convert_python_to_java(python_value)

Convert Python value to Java object for ArcadeDB.

This function only converts the types listed below explicitly. All other Python values (bool, int, float, str, bytes, and any Java objects) are returned as-is so that JPype performs the conversion automatically.

Supported Conversions:

Python Type Java Type
None null
Decimal BigDecimal
set HashSet
dict HashMap
list ArrayList
tuple ArrayList
datetime java.util.Date
date LocalDate

Notes:

  • datetime is converted to a java.util.Date built from its epoch milliseconds.
  • date is converted to a LocalDate. If the Java types are unavailable it is combined with time.min and converted as a datetime.
  • Collection elements, set members, and map keys/values are converted recursively.

Example:

from arcadedb_embedded.type_conversion import convert_python_to_java
from datetime import datetime
from decimal import Decimal

# Primitive types
java_int = convert_python_to_java(42)
java_str = convert_python_to_java("hello")
java_bool = convert_python_to_java(True)

# Date/time
java_datetime = convert_python_to_java(datetime.now())

# Decimal
java_decimal = convert_python_to_java(Decimal("123.456"))

# Collections
java_list = convert_python_to_java([1, 2, 3])
java_dict = convert_python_to_java({"key": "value"})

# Nested structures
java_nested = convert_python_to_java({
    "users": [
        {"name": "Alice", "age": 30},
        {"name": "Bob", "age": 25}
    ]
})

convert_java_to_python

from arcadedb_embedded.type_conversion import convert_java_to_python

python_value = convert_java_to_python(java_value)

Convert Java object to Python value.

Supported Conversions:

Java Type Python Type
null None
Boolean bool
String, Character str
Integer, Long, Short, Byte int
Float, Double float
BigDecimal Decimal
BigInteger int
java.util.Date datetime
LocalDate date
LocalDateTime datetime
Instant, ZonedDateTime datetime (UTC)
Map dict
Set set
List, Collection list

Notes:

  • Instant and ZonedDateTime are returned as timezone-aware datetime objects in UTC.
  • Other indexable Java objects (such as primitive arrays) are converted element-by-element to a list.
  • Unrecognized Java objects (for example Vertex, Edge, Document) are returned unchanged.

Example:

from arcadedb_embedded.type_conversion import convert_java_to_python

# Read from database (automatic conversion)
result = list(db.query("sql", "SELECT FROM User WHERE username = 'alice'"))[0]

# Get Python properties (automatically converted)
username = result.get("username")  # str
age = result.get("age")            # int
tags = result.get("tags")          # list
profile = result.get("profile")    # dict

# Manual conversion (if needed for custom types)
element = result.get_element()
java_property = element.get("username")  # Uses wrapper method
# Properties are automatically converted

Type-Specific Conversions

Integers

from arcadedb_embedded.type_conversion import convert_python_to_java

# Small integers → Integer
java_int = convert_python_to_java(42)          # Java Integer

# Large integers → Long
java_long = convert_python_to_java(2**40)     # Java Long

# Reading integers with SQL parameter binding
with db.transaction():
    db.command("sql", "INSERT INTO User SET age = ?", 30)  # Python int → Java Integer

age = db.query("sql", "SELECT age FROM User LIMIT 1").first().get("age")

Floats and Decimals

from arcadedb_embedded.type_conversion import convert_python_to_java
from decimal import Decimal

# Float → Double
java_double = convert_python_to_java(3.14159)

# Decimal → BigDecimal (for precision)
java_decimal = convert_python_to_java(Decimal("123.456789"))

# Reading with SQL parameter binding
with db.transaction():
    db.command(
        "sql",
        "INSERT INTO Product SET price = ?, tax = ?",
        19.99,
        Decimal("1.23"),
    )

row = db.query("sql", "SELECT price, tax FROM Product LIMIT 1").first()
price = row.get("price")                       # Double → float
tax = row.get("tax")                           # BigDecimal → Decimal

Strings

# String conversion with SQL parameter binding
with db.transaction():
    db.command(
        "sql",
        "INSERT INTO User SET name = ?, emoji = ?",
        "Alice",
        "Hello 👋 World 🌍",
    )

row = db.query("sql", "SELECT name, emoji FROM User LIMIT 1").first()
name = row.get("name")                         # String → str
emoji = row.get("emoji")                       # Full Unicode support

Dates and Times

from datetime import datetime, date, time

# datetime/date/time with SQL parameter binding
with db.transaction():
    db.command(
        "sql",
        "INSERT INTO Event SET timestamp = ?, birthDate = ?, startTime = ?",
        datetime.now(),
        date(1990, 1, 15),
        time(14, 30, 0),
    )

row = db.query("sql", "SELECT timestamp, birthDate, startTime FROM Event LIMIT 1").first()
timestamp = row.get("timestamp")               # datetime
birth_date = row.get("birthDate")              # date
start_time = row.get("startTime")              # time

Why some examples use set()

This page documents record property conversion behavior directly, so some examples use wrapper/property APIs to make the conversion boundary explicit. For normal application CRUD, prefer SQL/OpenCypher with bound parameters.

Binary Data

# bytes → byte[] (requires an active transaction)
binary_data = b"Hello World"
with db.transaction():
    vertex = db.new_vertex("File")
    vertex.set("data", binary_data)

    # Reading back
    data = vertex.get("data")                  # bytes
    print(data.decode("utf-8"))                # "Hello World"

Lists and Sets

# list → ArrayList (requires an active transaction)
with db.transaction():
    vertex = db.new_vertex("User")
    vertex.set("tags", ["python", "java", "database"])
    tags = vertex.get("tags")                  # list

    # set → HashSet
    vertex.set("roles", {"admin", "user"})
    roles = vertex.get("roles")                # set

    # Nested lists
    vertex.set("matrix", [[1, 2], [3, 4]])
    matrix = vertex.get("matrix")              # [[1, 2], [3, 4]]

Dictionaries (Maps)

# dict → HashMap (requires an active transaction)
with db.transaction():
    vertex = db.new_vertex("User")
    vertex.set("profile", {
        "firstName": "Alice",
        "lastName": "Smith",
        "address": {
            "city": "New York",
            "zip": "10001"
        }
    })

    # Reading back
    profile = vertex.get("profile")            # dict
    print(profile["firstName"])                # "Alice"
    print(profile["address"]["city"])          # "New York"

Query Parameter Conversion

from datetime import datetime

# Query parameters are automatically converted
results = db.query(
    "sql",
    "SELECT FROM User WHERE age > ? AND createdAt > ?",
    25, datetime(2024, 1, 1)  # Python types converted to Java
)

results = db.query(
    "sql",
    "SELECT FROM User WHERE age IN :ages",
    {"ages": [25, 30, 35]}  # dict/list converted to Java Map/List
)

# Reading results (automatically converted back to Python)
for result in results:
    name = result.get("name")         # str
    age = result.get("age")           # int
    created = result.get("createdAt") # datetime

Collection Type Preservation

# Lists preserve order (requires an active transaction)
with db.transaction():
    vertex = db.new_vertex("Sequence")
    vertex.set("numbers", [3, 1, 4, 1, 5, 9])
    numbers = vertex.get("numbers")            # [3, 1, 4, 1, 5, 9]

    # Sets remove duplicates
    vertex.set("unique", {3, 1, 4, 1, 5, 9})
    unique = vertex.get("unique")              # {1, 3, 4, 5, 9}

    # Dicts preserve keys
    vertex.set("mapping", {"a": 1, "b": 2, "c": 3})
    mapping = vertex.get("mapping")            # {"a": 1, "b": 2, "c": 3}

Complete Example

import arcadedb_embedded as arcadedb
from datetime import datetime, date
from decimal import Decimal

# Create database
db = arcadedb.create_database("./type_demo")

# Create schema (auto-transactional)
db.command("sql", "CREATE VERTEX TYPE Product")

# Test all type conversions
with db.transaction():
    product = db.new_vertex("Product")

    # Primitives
    product.set("productId", 12345)                    # int
    product.set("name", "Laptop")                      # str
    product.set("inStock", True)                       # bool
    product.set("price", 999.99)                       # float
    product.set("tax", Decimal("50.00"))               # Decimal

    # Date/time
    product.set("createdAt", datetime.now())           # datetime
    product.set("releaseDate", date(2024, 1, 15))      # date

    # Collections
    product.set("tags", ["electronics", "laptop"])     # list
    product.set("categories", {"computers", "tech"})   # set

    # Nested structures
    product.set("specs", {
        "cpu": "Intel i7",
        "ram": "16GB",
        "storage": ["512GB SSD", "1TB HDD"]
    })                                                  # dict

    # Binary
    product.set("thumbnail", b"PNG\x89...")            # bytes

    product.save()

# Read back and verify types
results = list(db.query("sql", "SELECT FROM Product"))
product = results[0]

print(f"Product ID: {product.get('productId')} ({type(product.get('productId')).__name__})")
print(f"Name: {product.get('name')} ({type(product.get('name')).__name__})")
print(f"In Stock: {product.get('inStock')} ({type(product.get('inStock')).__name__})")
print(f"Price: {product.get('price')} ({type(product.get('price')).__name__})")
print(f"Tax: {product.get('tax')} ({type(product.get('tax')).__name__})")
print(f"Created At: {product.get('createdAt')} ({type(product.get('createdAt')).__name__})")
print(f"Tags: {product.get('tags')} ({type(product.get('tags')).__name__})")

db.close()

Output:

Product ID: 12345 (int)
Name: Laptop (str)
In Stock: True (bool)
Price: 999.99 (float)
Tax: 50.00 (Decimal)
Created At: 2024-01-15 10:30:45.123456 (datetime)
Tags: ['electronics', 'laptop'] (list)

Manual Conversion

Most of the time, conversions happen automatically. But you can manually convert when needed:

from arcadedb_embedded.type_conversion import convert_python_to_java, convert_java_to_python

# Manual Python → Java
python_data = {"name": "Alice", "age": 30}
java_map = convert_python_to_java(python_data)

# Use Java object directly (requires an active transaction)
with db.transaction():
    java_record = db.new_vertex("User")._java_object
    java_record.set("profile", java_map)

# Manual Java → Python
java_property = java_record.get("profile")
python_dict = convert_java_to_python(java_property)
print(python_dict)  # {"name": "Alice", "age": 30}

Best Practices

1. Use Native Python Types

# ✅ Good: Use Python types
vertex.set("tags", ["python", "java"])
vertex.set("count", 42)
vertex.set("timestamp", datetime.now())

# ❌ Bad: Manually convert (unnecessary)
from arcadedb_embedded.type_conversion import convert_python_to_java
vertex.set("tags", convert_python_to_java(["python", "java"]))  # Redundant

2. Use Decimal for Money

from decimal import Decimal

# ✅ Good: Decimal for precise values
vertex.set("price", Decimal("19.99"))
vertex.set("tax", Decimal("1.60"))

# ❌ Bad: Float for money (precision loss)
vertex.set("price", 19.99)  # May lose precision

3. Consistent Collection Types

# ✅ Good: Consistent types
vertex.set("tags", ["tag1", "tag2", "tag3"])  # All strings

# ⚠️ Mixed types work but can be confusing
vertex.set("mixed", [1, "two", 3.0, True])

4. Handle None Values

# ✅ Good: Check for None
age = vertex.get("age")
if age is not None:
    print(f"Age: {age}")
else:
    print("Age not set")

# ❌ Bad: Assume value exists
print(f"Age: {vertex.get('age')}")  # May be None

Type Conversion Limitations

1. Custom Python Classes

# ❌ Custom classes not supported
class MyClass:
    def __init__(self, value):
        self.value = value

vertex.set("custom", MyClass(42))  # ❌ Error

# ✅ Convert to dict first
vertex.set("custom", {"value": 42})  # ✅ Works

2. Complex Nested Structures

# ✅ Reasonable nesting works
vertex.set("data", {
    "level1": {
        "level2": {
            "level3": [1, 2, 3]
        }
    }
})

# ⚠️ Very deep nesting may impact performance

3. Large Binary Data

# ✅ Small binary data
vertex.set("icon", b"PNG\x89...")  # OK

# ⚠️ Large binary data (consider file storage)
with open("large_file.bin", "rb") as f:
    data = f.read()  # May be very large
    vertex.set("file", data)  # May impact memory

Troubleshooting

Type Mismatch Errors

# ❌ Error: Expected list, got dict
vertex.set("tags", {"tag1": 1})  # Wrong type

# ✅ Fix: Use correct type
vertex.set("tags", ["tag1"])

Precision Loss with Floats

# ⚠️ Float precision issues
price = 19.99
tax = 0.01
total = price + tax  # May not be exactly 20.00

# ✅ Use Decimal
from decimal import Decimal
price = Decimal("19.99")
tax = Decimal("0.01")
total = price + tax  # Exactly 20.00

Date/Time Timezone Issues

from datetime import datetime, timezone

# ⚠️ Naive datetime (no timezone)
now = datetime.now()  # Local time, no timezone

# ✅ Aware datetime (with timezone)
now_utc = datetime.now(timezone.utc)  # UTC time

See Also