Skip to content

Conversation

@yht0827
Copy link

@yht0827 yht0827 commented Jan 23, 2026

Fixes: #10333

Purpose of this pull request

When using JsonPath transform with multiple date/timestamp columns, the converter received null as the field name, causing incorrect date format resolution.
Fixed by passing columnConfig.getDestField() to the converter, ensuring each column resolves its own date format correctly.

Does this PR introduce any user-facing change?

Yes. Previously, multiple date/timestamp columns in JsonPath transform could fail or produce incorrect results due to missing field name context. Now each column correctly applies its configured date/time format.

How was this patch tested?

  • JsonPathTransformTest.java
    • testMultipleDateColumnsWithDifferentFormats() - Verifies multiple date columns with distinct formats are correctly converted using their respective dest_field configurations.
    • testMultipleDateColumnsWithNullValues() - Ensures null values in date columns are preserved without causing conversion errors.

Check list

@dybyte
Copy link
Collaborator

dybyte commented Jan 23, 2026

Please enable CI following the instructions.

Copy link
Collaborator

@dybyte dybyte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add null checks in JsonRowConverters#convertToLocalDate and JsonRowConverters#convertToLocalDateTime.
Also, could you add test cases for these two methods in JsonRowDataSerDeSchemaTest?

@zhangshenghang
Copy link
Member

There is currently a problem with CI, and you need to enable it.

@yht0827
Copy link
Author

yht0827 commented Jan 24, 2026

@dybyte

Thanks for the review!

I’ll add null checks to ‎JsonRowConverters#convertToLocalDate and ‎JsonRowConverters#convertToLocalDateTime, and also add test cases for these methods in ‎JsonRowDataSerDeSchemaTest.

@DanielCarter-stack
Copy link

DanielCarter-stack commented Jan 24, 2026

Issue 1: Test coverage is not comprehensive enough

Location: seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/JsonPathTransformTest.java:549-662

Related Context:

  • Test class: JsonPathTransformTest.java
  • Production class: JsonPathTransform.java:130-165
  • Dependency class: JsonToRowConverters.java:259-293

Problem Description:

The newly added test cases cover the scenarios of "multiple date columns with different formats" and "containing null values", but are missing the following scenarios:

  1. Regression test for a single date column (to verify the modification does not break original behavior)
  2. Test for multiple date columns with the same format (to verify caching mechanism)
  3. Mixed type field test (to verify it does not affect string, integer, and other types)

Potential Risks:

  • Risk 1: Unable to ensure compatibility of the modification with single date column scenarios
  • Risk 2: Unable to verify whether performance optimization for multiple fields with the same format has regressed
  • Risk 3: Unable to verify the modification does not affect non-date type fields

Impact Scope:

  • Direct impact: Test coverage of JsonPathTransform
  • Indirect impact: Safety net for future refactoring
  • Impact surface: Single Transform (not framework-level)

Severity: MINOR

Improvement Suggestions:

@Test
public void testSingleDateColumn() {
    // Verify that the behavior of a single date column remains consistent with the modifications
    Map<String, Object> configMap = new HashMap<>();
    configMap.put(
        JsonPathTransformConfig.COLUMNS.key(),
        Arrays.asList(
            ImmutableMap.of(
                JsonPathTransformConfig.SRC_FIELD.key(), "data",
                JsonPathTransformConfig.PATH.key(), "$.birth",
                JsonPathTransformConfig.DEST_FIELD.key(), "birth_date",
                JsonPathTransformConfig.DEST_TYPE.key(), "date")));
    // ... test logic
}

@Test
public void testMultipleDateColumnsWithSameFormat() {
    // Verify that multiple date columns with the same format can share cache
    Map<String, Object> configMap = new HashMap<>();
    configMap.put(
        JsonPathTransformConfig.COLUMNS.key(),
        Arrays.asList(
            ImmutableMap.of(..., "birth", "birth_date", "date"),
            ImmutableMap.of(..., "hired", "hire_date", "date")));
    String jsonData = "{\"birth\": \"2024-01-15\", \"hired\": \"2024-02-20\"}";
    // ... verify both fields are correctly parsed
}

@Test
public void testMixedTypeColumns() {
    // Verify mixed configuration of date columns with string and integer columns
    Map<String, Object> configMap = new HashMap<>();
    configMap.put(
        JsonPathTransformConfig.COLUMNS.key(),
        Arrays.asList(
            ImmutableMap.of(..., "date_field", "date"),
            ImmutableMap.of(..., "string_field", "string"),
            ImmutableMap.of(..., "int_field", "int")));
    // ... verify all types are correctly converted
}

Rationale:

  • These tests can enhance confidence in the correctness of the modification
  • Single date column test ensures backward compatibility
  • Same format test verifies cache sharing works properly
  • Mixed type test verifies isolation of the modification (does not affect other types)

Issue 4: Insufficient encapsulability of fieldFormatterMap

Location: seatunnel-formats/seatunnel-format-json/src/main/java/org/apache/seatunnel/format/json/JsonToRowConverters.java:80

Related Context:

  • Current definition: public Map<String, DateTimeFormatter> fieldFormatterMap = new HashMap<>();
  • Usage location: JsonToRowConverters.java:261, 281
  • Caller: JsonPathTransform.java (indirectly through converter)

Problem Description:

fieldFormatterMap is declared as public, which is an unnecessary encapsulation violation. Although it does need to be accessed externally (e.g., by tests), a better approach is to provide a controlled access method.

Potential Risks:

  • Risk 1: External code may directly modify the Map, causing cache pollution
  • Risk 2: Difficult to change internal implementation during future refactoring (e.g., replacing with ConcurrentMap)

Impact Scope:

  • Direct impact: API design of JsonToRowConverters
  • Indirect impact: All code using JsonToRowConverters
  • Impact surface: Format layer (multiple Formats use similar patterns)

Severity: MINOR

Improvement Suggestions:

// Current (line 80)
public Map<String, DateTimeFormatter> fieldFormatterMap = new HashMap<>();

// Suggested change to
private Map<String, DateTimeFormatter> fieldFormatterMap = new HashMap<>();

// If tests need access, add package-level visible access methods
Map<String, DateTimeFormatter> getFieldFormatterMap() {
    return fieldFormatterMap;
}

// Or provide controlled access methods
public DateTimeFormatter getCachedFormatter(String fieldName) {
    return fieldFormatterMap.get(fieldName);
}

Rationale:

  • Follows encapsulation principle: internal implementation details should not be exposed as public
  • Prevents accidental modification: external code should not directly manipulate cache
  • Preserves refactoring freedom: underlying implementation can be replaced in the future without affecting API

Note: This issue is outside the scope of this PR and is an existing problem in the codebase. However, since it was discovered during review, it should be recorded for future improvement.


@yht0827
Copy link
Author

yht0827 commented Jan 26, 2026

@dybyte @DanielCarter-stack Thanks for the detailed reviews!

I've added the following test cases to cover the requested scenarios:

JsonPathTransformTest

Test Method Purpose
testMultipleDateColumnsWithDifferentFormats Multiple date/timestamp columns with different formats
testMultipleDateColumnsWithNullValues Null values don't cause conversion errors
testSingleDateColumn Regression test - single date column still works
testMultipleDateColumnsWithSameFormat Same format columns can share cache
testMixedTypeColumns Mixed types (date + string + int) work correctly
testInvalidDateFormat Invalid date format throws exception
testEmptyStringDateValue Empty string throws exception
testLeapYearDate Leap year (2024-02-29) parses correctly

JsonRowDataSerDeSchemaTest

Test Method Purpose
testConvertToLocalDateWithEmptyString Empty string handling for LocalDate conversion
testConvertToLocalDateTimeWithEmptyString Empty string handling for LocalDateTime conversion

@DanielCarter-stack I'm also interested in working on issue 4. Will create a follow-up PR after this one is merged.

Please review when you have time.

@github-actions github-actions bot added the Zeta label Jan 28, 2026
@github-actions github-actions bot removed the Zeta label Jan 28, 2026
Comment on lines 259 to +265
private LocalDate convertToLocalDate(JsonNode jsonNode, String fieldName) {
String dateStr = jsonNode.asText();

if (dateStr == null || dateStr.trim().isEmpty()) {
return null;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should check fieldName for null rather than dateStr.
If fieldName is null, we shouldn’t put this entry into fieldFormatterMap.

Comment on lines +285 to +289

if (datetimeStr == null || datetimeStr.trim().isEmpty()) {
return null;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Comment on lines +717 to +765
@Test
public void testConvertToLocalDateWithEmptyString() throws IOException {
SeaTunnelRowType rowType =
new SeaTunnelRowType(
new String[] {"date_field"},
new SeaTunnelDataType<?>[] {LocalTimeType.LOCAL_DATE_TYPE});
JsonDeserializationSchema deserializationSchema =
new JsonDeserializationSchema(false, false, rowType);

// Test empty string
String emptyDateJson = "{\"date_field\":\"\"}";
SeaTunnelRow row = deserializationSchema.deserialize(emptyDateJson.getBytes());
assertNull(row.getField(0));

// Test whitespace only string
String whitespaceJson = "{\"date_field\":\" \"}";
row = deserializationSchema.deserialize(whitespaceJson.getBytes());
assertNull(row.getField(0));

// Test normal date value still works
String normalDateJson = "{\"date_field\":\"2024-01-15\"}";
row = deserializationSchema.deserialize(normalDateJson.getBytes());
assertEquals(LocalDate.of(2024, 1, 15), row.getField(0));
}

@Test
public void testConvertToLocalDateTimeWithEmptyString() throws IOException {
SeaTunnelRowType rowType =
new SeaTunnelRowType(
new String[] {"timestamp_field"},
new SeaTunnelDataType<?>[] {LocalTimeType.LOCAL_DATE_TIME_TYPE});
JsonDeserializationSchema deserializationSchema =
new JsonDeserializationSchema(false, false, rowType);

// Test empty string
String emptyTimestampJson = "{\"timestamp_field\":\"\"}";
SeaTunnelRow row = deserializationSchema.deserialize(emptyTimestampJson.getBytes());
assertNull(row.getField(0));

// Test whitespace only string
String whitespaceJson = "{\"timestamp_field\":\" \"}";
row = deserializationSchema.deserialize(whitespaceJson.getBytes());
assertNull(row.getField(0));

// Test normal timestamp value still works
String normalTimestampJson = "{\"timestamp_field\":\"2024-01-15 10:30:00\"}";
row = deserializationSchema.deserialize(normalTimestampJson.getBytes());
assertEquals(LocalDateTime.of(2024, 1, 15, 10, 30, 0), row.getField(0));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these tests related to the current PR? I’m not entirely sure about the connection.

Comment on lines +795 to +796
Assertions.assertThrows(
Exception.class, () -> transform.map(new SeaTunnelRow(new Object[] {jsonData})));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use a more specific exception type here instead of Exception.class?

Comment on lines +821 to +822
Assertions.assertThrows(
Exception.class, () -> transform.map(new SeaTunnelRow(new Object[] {jsonData})));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] JsonPath convert datetime/date error

4 participants