This is an automated email from the ASF dual-hosted git repository.
luchunliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git
The following commit(s) were added to refs/heads/master by this push:
new 531ce2a988 [INLONG-12189][SDK] Support Map and JsonObject key access
via [...] in ArrayParser (#12190)
531ce2a988 is described below
commit 531ce2a9882165838d5e09ad816a050328be7fb3
Author: ChunLiang Lu <[email protected]>
AuthorDate: Tue Aug 25 19:57:14 2026 +0800
[INLONG-12189][SDK] Support Map and JsonObject key access via [...] in
ArrayParser (#12190)
* [INLONG-12189][SDK] Support Map and JsonObject key access via [...] in
ArrayParser
* fix spotless
---
.../sdk/transform/process/parser/ArrayParser.java | 28 +++++
.../processor/TestCsv2RowDataProcessor.java | 133 +++++++++++++++++++++
2 files changed, 161 insertions(+)
diff --git
a/inlong-sdk/transform-sdk/src/main/java/org/apache/inlong/sdk/transform/process/parser/ArrayParser.java
b/inlong-sdk/transform-sdk/src/main/java/org/apache/inlong/sdk/transform/process/parser/ArrayParser.java
index 46c2a2689e..1552c9903a 100644
---
a/inlong-sdk/transform-sdk/src/main/java/org/apache/inlong/sdk/transform/process/parser/ArrayParser.java
+++
b/inlong-sdk/transform-sdk/src/main/java/org/apache/inlong/sdk/transform/process/parser/ArrayParser.java
@@ -23,10 +23,12 @@ import
org.apache.inlong.sdk.transform.process.operator.OperatorTools;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.sf.jsqlparser.expression.ArrayExpression;
import java.util.List;
+import java.util.Map;
/**
* ArrayParser
@@ -54,6 +56,10 @@ public class ArrayParser implements ValueParser {
Number rightObj = (Number) rightValue;
return leftObj.get(rightObj.intValue());
}
+ if (leftValue instanceof Map<?, ?>) {
+ Map<?, ?> leftObj = (Map<?, ?>) leftValue;
+ return leftObj.get(rightValue);
+ }
if (leftValue instanceof JsonArray && rightValue instanceof Number) {
JsonArray leftObj = (JsonArray) leftValue;
Number rightObj = (Number) rightValue;
@@ -76,6 +82,28 @@ public class ArrayParser implements ValueParser {
return result;
}
}
+ if (leftValue instanceof JsonObject && rightValue instanceof String) {
+ JsonObject leftObj = (JsonObject) leftValue;
+ String rightObj = (String) rightValue;
+ JsonElement result = leftObj.get(rightObj);
+ if (result.isJsonNull()) {
+ return null;
+ }
+ if (result.isJsonPrimitive()) {
+ JsonPrimitive jsonPrim = (JsonPrimitive) result;
+ if (jsonPrim.isString()) {
+ return jsonPrim.getAsString();
+ } else if (jsonPrim.isBoolean()) {
+ return jsonPrim.getAsBoolean();
+ } else if (jsonPrim.isNumber()) {
+ return jsonPrim.getAsNumber();
+ }
+ return jsonPrim.toString();
+ }
+ if (result.isJsonArray() || result.isJsonObject()) {
+ return result;
+ }
+ }
return null;
}
}
diff --git
a/inlong-sdk/transform-sdk/src/test/java/org/apache/inlong/sdk/transform/process/processor/TestCsv2RowDataProcessor.java
b/inlong-sdk/transform-sdk/src/test/java/org/apache/inlong/sdk/transform/process/processor/TestCsv2RowDataProcessor.java
new file mode 100644
index 0000000000..029295fcdd
--- /dev/null
+++
b/inlong-sdk/transform-sdk/src/test/java/org/apache/inlong/sdk/transform/process/processor/TestCsv2RowDataProcessor.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.sdk.transform.process.processor;
+
+import org.apache.inlong.sdk.transform.decode.SourceDecoderFactory;
+import org.apache.inlong.sdk.transform.encode.SinkEncoderFactory;
+import org.apache.inlong.sdk.transform.pojo.CsvSourceInfo;
+import org.apache.inlong.sdk.transform.pojo.FieldInfo;
+import org.apache.inlong.sdk.transform.pojo.RowDataSinkInfo;
+import org.apache.inlong.sdk.transform.pojo.TransformConfig;
+import org.apache.inlong.sdk.transform.process.TransformProcessor;
+
+import org.apache.flink.table.data.RowData;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * TestCsv2RowDataProcessor
+ * <p>
+ * Verifies that CSV source data can be transformed into {@link RowData} via
SQL that:
+ * <ul>
+ * <li>selects a small set of source columns as-is;</li>
+ * <li>URL-decodes the {@code event_value} column and turns it into a KV map
with
+ * {@code STR_TO_MAP(URL_DECODE(event_value), '&', '=')}, then extracts
the
+ * {@code HY50} entry via bracket key access;</li>
+ * <li>filters rows with {@code HY50 = 'welfare_milestone_operations'} and
+ * {@code event_code = 'OnPageEnter'}.</li>
+ * </ul>
+ * The supplied test payload has {@code event_code=OnPageMod} and HY50 value
+ * {@code ActivityPage}, so the WHERE clause filters the record out and the
+ * sink produces zero rows.
+ */
+public class TestCsv2RowDataProcessor extends AbstractProcessorTestBase {
+
+ @Test
+ public void testCsv2RowDataHitHY50AndEventCode() throws Exception {
+ List<FieldInfo> sourceFields = this.getTestFieldList(
+ "ftime", "extinfo", "event_code", "event_value");
+ List<FieldInfo> sinkFields = this.getTestFieldList(
+ "ftime", "extinfo", "event_code", "HY50");
+
+ CsvSourceInfo csvSource = new CsvSourceInfo("UTF-8", '|', '\\',
sourceFields);
+ RowDataSinkInfo rowSink = new RowDataSinkInfo("UTF-8", sinkFields);
+
+ String transformSql = "select "
+ + "ftime as ftime,"
+ + "extinfo as extinfo,"
+ + "event_code as event_code,"
+ + "STR_TO_MAP(URL_DECODE(event_value), '&', '=')['HY50'] as
HY50 "
+ + "from source "
+ + "WHERE STR_TO_MAP(URL_DECODE(event_value), '&',
'=')['HY50'] = 'welfare_milestone_operations' "
+ + "AND event_code = 'OnPageEnter'";
+
+ TransformProcessor<String, RowData> processor =
TransformProcessor.create(
+ new TransformConfig(transformSql),
+ SourceDecoderFactory.createCsvDecoder(csvSource),
+ SinkEncoderFactory.createRowEncoder(rowSink));
+
+ // Positive sample: satisfies BOTH WHERE conditions.
+ String testData = "2026-08-20 12:48:56.929"
+ + "|extinfo=127.0.0.1"
+ + "|OnPageEnter"
+ + "|HY50%3Dwelfare_milestone_operations";
+
+ List<RowData> output = processor.transform(testData, new HashMap<>());
+ Assert.assertEquals(1, output.size());
+ Assert.assertEquals("2026-08-20 12:48:56.929",
output.get(0).getString(0).toString());
+ Assert.assertEquals("extinfo=127.0.0.1",
output.get(0).getString(1).toString());
+ Assert.assertEquals("OnPageEnter",
output.get(0).getString(2).toString());
+ Assert.assertEquals("welfare_milestone_operations",
output.get(0).getString(3).toString());
+ }
+
+ @Test
+ public void testCsv2RowDataFilteredByHY50AndEventCode() throws Exception {
+ // ==== source fields: 4 columns aligned with the pipe-delimited
payload ====
+ List<FieldInfo> sourceFields = this.getTestFieldList(
+ "ftime", "extinfo", "event_code", "event_value");
+
+ // ==== sink fields: 3 pass-through columns + extracted HY50 ====
+ List<FieldInfo> sinkFields = this.getTestFieldList(
+ "ftime", "extinfo", "event_code", "HY50");
+
+ CsvSourceInfo csvSource = new CsvSourceInfo("UTF-8", '|', '\\',
sourceFields);
+ RowDataSinkInfo rowSink = new RowDataSinkInfo("UTF-8", sinkFields);
+
+ String transformSql = "select "
+ + "ftime as ftime,"
+ + "extinfo as extinfo,"
+ + "event_code as event_code,"
+ + "STR_TO_MAP(URL_DECODE(event_value), '&', '=')['HY50'] as
HY50 "
+ + "from source "
+ + "WHERE STR_TO_MAP(URL_DECODE(event_value), '&',
'=')['HY50'] = 'welfare_milestone_operations' "
+ + "AND event_code = 'OnPageEnter'";
+
+ TransformConfig config = new TransformConfig(transformSql);
+ TransformProcessor<String, RowData> processor =
TransformProcessor.create(
+ config,
+ SourceDecoderFactory.createCsvDecoder(csvSource),
+ SinkEncoderFactory.createRowEncoder(rowSink));
+
+ // Note: this record does NOT satisfy the WHERE clause:
+ // - event_code = 'OnPageMod' (not 'OnPageEnter')
+ // - HY50 in the decoded map = 'ActivityPage' (not
'welfare_milestone_operations')
+ // event_value only carries a single URL-encoded HY50 KV entry.
+ String testData = "2026-08-20 12:48:56.929"
+ + "|extinfo=127.0.0.1"
+ + "|OnPageMod"
+ + "|HY50%3DActivityPage";
+
+ List<RowData> output = processor.transform(testData, new HashMap<>());
+
+ // The record fails BOTH WHERE conditions, so nothing is emitted.
+ Assert.assertEquals(0, output.size());
+ }
+}