diff --git a/core/common/lib/sql-lib/src/main/java/org/eclipse/edc/sql/translation/JsonArrayTranslator.java b/core/common/lib/sql-lib/src/main/java/org/eclipse/edc/sql/translation/JsonArrayTranslator.java
new file mode 100644
index 00000000000..3030b507bd9
--- /dev/null
+++ b/core/common/lib/sql-lib/src/main/java/org/eclipse/edc/sql/translation/JsonArrayTranslator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2025 Cofinity-X
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Cofinity-X - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.sql.translation;
+
+import org.eclipse.edc.spi.query.Criterion;
+import org.eclipse.edc.util.reflection.PathItem;
+
+import java.util.Collection;
+import java.util.List;
+
+import static org.eclipse.edc.sql.translation.FieldTranslator.toParameters;
+import static org.eclipse.edc.sql.translation.FieldTranslator.toValuePlaceholder;
+
+/**
+ * This is a specialized translator, that targets object properties that are JSON array, for example {@code someObject.jsonArray},
+ * and a criterion of "jsonArray contains foobar".
+ *
+ * This is necessary, because with a conventional {@link JsonFieldTranslator} we would get {@code (jsonArray -> 'jsonArray')::jsonb ?? ?},
+ * because it always assumes one indirection from columnName to object name.
+ *
+ * The {@link JsonArrayTranslator} explicitly ignores this indirection.
+ */
+public class JsonArrayTranslator implements FieldTranslator {
+
+ @Override
+ public String getLeftOperand(List path, Class> rightOperandType) {
+ if (Collection.class.isAssignableFrom(rightOperandType)) {
+ throw new IllegalArgumentException("JsonArrayTranslator only supports scalar right-operands, found '%s' ".formatted(rightOperandType));
+ }
+ if (path.size() == 1) {
+ return path.get(0).toString();
+ }
+ throw new IllegalArgumentException("Invalid path for JsonArrayTranslator: must have one element, but found '%s'".formatted(path.size()));
+ }
+
+ @Override
+ public WhereClause toWhereClause(List path, Criterion criterion, SqlOperator operator) {
+ var leftOperand = getLeftOperand(path, criterion.getOperandRight().getClass());
+
+ if (!operator.representation().equals("??")) {
+ throw new IllegalArgumentException("Invalid operator for JsonArrayTranslator: must be '??', but was '%s'".formatted(operator.representation()));
+ }
+
+ return new WhereClause("%s::jsonb ?? %s".formatted(leftOperand, toValuePlaceholder(criterion)), toParameters(criterion));
+ }
+}
diff --git a/core/common/lib/sql-lib/src/test/java/org/eclipse/edc/sql/translation/JsonArrayTranslatorTest.java b/core/common/lib/sql-lib/src/test/java/org/eclipse/edc/sql/translation/JsonArrayTranslatorTest.java
new file mode 100644
index 00000000000..d94ea150d99
--- /dev/null
+++ b/core/common/lib/sql-lib/src/test/java/org/eclipse/edc/sql/translation/JsonArrayTranslatorTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2025 Cofinity-X
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Cofinity-X - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.sql.translation;
+
+import org.eclipse.edc.util.reflection.PathItem;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.eclipse.edc.spi.query.Criterion.criterion;
+
+class JsonArrayTranslatorTest {
+
+ private final JsonArrayTranslator translator = new JsonArrayTranslator();
+
+ @Test
+ void getLeftOperand() {
+ var result = translator.getLeftOperand(PathItem.parse("array"), Object.class);
+ assertThat(result).isEqualTo("array");
+ }
+
+ @Test
+ void getLeftOperand_invalidPath() {
+ assertThatThrownBy(() -> translator.getLeftOperand(PathItem.parse("someobject.array"), Object.class))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ void toWhereClause() {
+ var operator = new SqlOperator("??", Object.class);
+ var criterion = criterion("array", "contains", "value");
+
+ var result = translator.toWhereClause(PathItem.parse("array"), criterion, operator);
+
+ assertThat(result.sql()).isEqualTo("array::jsonb ?? ?");
+ assertThat(result.parameters()).containsExactly("value");
+ }
+
+ @Test
+ void toWhereClause_rightOperandIsList_invalid() {
+ var operator = new SqlOperator("??", Object.class);
+ var criterion = criterion("array", "contains", List.of("val1", "val2"));
+
+ assertThatThrownBy(() -> translator.toWhereClause(PathItem.parse("array"), criterion, operator))
+ .isInstanceOf(IllegalArgumentException.class);
+
+ }
+
+ @Test
+ void toWhereClause_invalidOperator() {
+ var operator = new SqlOperator("=", Object.class);
+ var criterion = criterion("array", "contains", "value");
+
+
+ assertThatThrownBy(() -> translator.toWhereClause(PathItem.parse("array"), criterion, operator))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+}
\ No newline at end of file