You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by "zhangliang.apache.org via notifications" <no...@shardingsphere.apache.org> on 2026/10/31 08:38:09 UTC

(shardingsphere) branch master updated: Use existing engines for dynamic JSON and YAML serialization (#40432)

This is an automated email from the ASF dual-hosted git repository.

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 4505cb5594a Use existing engines for dynamic JSON and YAML serialization (#40432)
4505cb5594a is described below

commit 4505cb5594a905b3e22878b986c13510cf9e2aba
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Sat Oct 31 16:38:02 2026 +0800

    Use existing engines for dynamic JSON and YAML serialization (#40432)
---
 .../export/DatabaseExportMetaDataGenerator.java    | 29 ++++-----
 .../MigrationJobExecutorCallbackTest.java          | 31 ++++-----
 .../server/http/StreamableHttpMCPServlet.java      |  6 +-
 .../server/http/StreamableHttpMCPServletTest.java  | 49 +++++++++++++-
 .../util/DatabaseExportMetaDataGeneratorTest.java  | 75 ++++++++++++++++++++++
 .../framework/helper/PipelineCaseHelper.java       |  4 +-
 .../type/GitHubTestParameterLoadStrategyTest.java  | 37 ++++++++---
 7 files changed, 179 insertions(+), 52 deletions(-)

diff --git a/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/configuration/export/DatabaseExportMetaDataGenerator.java b/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/configuration/export/DatabaseExportMetaDataGenerator.java
index 3bfc669f844..4dbd08ad8cd 100644
--- a/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/configuration/export/DatabaseExportMetaDataGenerator.java
+++ b/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/configuration/export/DatabaseExportMetaDataGenerator.java
@@ -31,6 +31,8 @@ import org.apache.shardingsphere.infra.yaml.config.swapper.rule.YamlRuleConfigur
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.Map.Entry;
 
 /**
@@ -55,33 +57,34 @@ public final class DatabaseExportMetaDataGenerator {
     }
     
     private void appendDatabaseName(final String databaseName, final StringBuilder stringBuilder) {
-        stringBuilder.append("databaseName: ").append(databaseName).append(System.lineSeparator());
+        stringBuilder.append(YamlEngine.marshal(Collections.singletonMap("databaseName", databaseName)));
     }
     
     private void appendDataSourceConfigurations(final ShardingSphereDatabase database, final StringBuilder stringBuilder) {
         if (database.getResourceMetaData().getStorageUnits().isEmpty()) {
             return;
         }
-        stringBuilder.append("dataSources:").append(System.lineSeparator());
+        Map<String, Map<String, Object>> dataSources = new LinkedHashMap<>(database.getResourceMetaData().getStorageUnits().size(), 1F);
         for (Entry<String, StorageUnit> entry : database.getResourceMetaData().getStorageUnits().entrySet()) {
-            appendDataSourceConfiguration(entry.getKey(), entry.getValue().getDataSourcePoolProperties(), stringBuilder);
+            Map<String, Object> props = createDataSourceConfiguration(entry.getValue().getDataSourcePoolProperties());
+            dataSources.put(entry.getKey(), props.isEmpty() ? null : props);
         }
+        stringBuilder.append(YamlEngine.marshal(Collections.singletonMap("dataSources", dataSources)));
     }
     
-    private void appendDataSourceConfiguration(final String dataSourceName, final DataSourcePoolProperties props, final StringBuilder stringBuilder) {
-        stringBuilder.append(createIndentation(2)).append(dataSourceName).append(':').append(System.lineSeparator());
+    private Map<String, Object> createDataSourceConfiguration(final DataSourcePoolProperties props) {
+        Map<String, Object> result = new LinkedHashMap<>(props.getConnectionPropertySynonyms().getStandardProperties().size() + props.getPoolPropertySynonyms().getStandardProperties().size(), 1F);
         for (Entry<String, Object> entry : props.getConnectionPropertySynonyms().getStandardProperties().entrySet()) {
             if (null != entry.getValue()) {
-                String value = entry.getValue().toString();
-                stringBuilder.append(createIndentation(4)).append(entry.getKey()).append(": ").append(value).append(System.lineSeparator());
+                result.put(entry.getKey(), entry.getValue());
             }
         }
         for (Entry<String, Object> entry : props.getPoolPropertySynonyms().getStandardProperties().entrySet()) {
             if (null != entry.getValue()) {
-                String value = entry.getValue().toString();
-                stringBuilder.append(createIndentation(4)).append(entry.getKey()).append(": ").append(value).append(System.lineSeparator());
+                result.put(entry.getKey(), entry.getValue());
             }
         }
+        return result;
     }
     
     @SuppressWarnings({"rawtypes", "unchecked"})
@@ -101,12 +104,4 @@ public final class DatabaseExportMetaDataGenerator {
             stringBuilder.append(YamlEngine.marshal(Collections.singletonList(entry.getValue().swapToYamlConfiguration(entry.getKey()))));
         }
     }
-    
-    private String createIndentation(final int count) {
-        StringBuilder result = new StringBuilder();
-        for (int i = 0; i < count; i++) {
-            result.append(" ");
-        }
-        return result.toString();
-    }
 }
diff --git a/kernel/data-pipeline/scenario/migration/core/src/test/java/org/apache/shardingsphere/data/pipeline/scenario/migration/MigrationJobExecutorCallbackTest.java b/kernel/data-pipeline/scenario/migration/core/src/test/java/org/apache/shardingsphere/data/pipeline/scenario/migration/MigrationJobExecutorCallbackTest.java
index ceaa77d6d91..909b182b64d 100644
--- a/kernel/data-pipeline/scenario/migration/core/src/test/java/org/apache/shardingsphere/data/pipeline/scenario/migration/MigrationJobExecutorCallbackTest.java
+++ b/kernel/data-pipeline/scenario/migration/core/src/test/java/org/apache/shardingsphere/data/pipeline/scenario/migration/MigrationJobExecutorCallbackTest.java
@@ -54,6 +54,7 @@ import org.apache.shardingsphere.infra.metadata.database.schema.QualifiedTable;
 import org.apache.shardingsphere.infra.metadata.identifier.ShardingSphereIdentifier;
 import org.apache.shardingsphere.infra.spi.type.ordered.OrderedSPILoader;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import org.apache.shardingsphere.infra.util.yaml.YamlEngine;
 import org.apache.shardingsphere.infra.yaml.config.pojo.YamlRootConfiguration;
 import org.apache.shardingsphere.mode.manager.ContextManager;
 import org.junit.jupiter.api.Test;
@@ -229,13 +230,18 @@ class MigrationJobExecutorCallbackTest {
     }
     
     private String createSourceDataSourceYaml(final int maxPoolSize, final int maximumPoolSize) {
-        return SOURCE_DATA_SOURCE_NAME + ":\n"
-                + "  dataSourceClassName: com.zaxxer.hikari.HikariDataSource\n"
-                + "  url: " + SOURCE_JDBC_URL + "\n"
-                + "  username: root\n"
-                + "  password: root\n"
-                + "  maxPoolSize: " + maxPoolSize + "\n"
-                + "  maximumPoolSize: " + maximumPoolSize + "\n";
+        return YamlEngine.marshal(Collections.singletonMap(SOURCE_DATA_SOURCE_NAME, createDataSourceProperties(SOURCE_JDBC_URL, maxPoolSize, maximumPoolSize)));
+    }
+    
+    private Map<String, Object> createDataSourceProperties(final String jdbcUrl, final int maxPoolSize, final int maximumPoolSize) {
+        Map<String, Object> result = new LinkedHashMap<>(6, 1F);
+        result.put("dataSourceClassName", "com.zaxxer.hikari.HikariDataSource");
+        result.put("url", jdbcUrl);
+        result.put("username", "root");
+        result.put("password", "root");
+        result.put("maxPoolSize", maxPoolSize);
+        result.put("maximumPoolSize", maximumPoolSize);
+        return result;
     }
     
     private void mockProxyContext(final MockedStatic<PipelineContextManager> pipelineContextManager) {
@@ -303,17 +309,6 @@ class MigrationJobExecutorCallbackTest {
         return result;
     }
     
-    private Map<String, Object> createDataSourceProperties(final String jdbcUrl, final int maxPoolSize, final int maximumPoolSize) {
-        Map<String, Object> result = new LinkedHashMap<>(6, 1F);
-        result.put("dataSourceClassName", "com.zaxxer.hikari.HikariDataSource");
-        result.put("url", jdbcUrl);
-        result.put("username", "root");
-        result.put("password", "root");
-        result.put("maxPoolSize", maxPoolSize);
-        result.put("maximumPoolSize", maximumPoolSize);
-        return result;
-    }
-    
     private TransmissionProcessContext mockProcessContext() {
         TransmissionProcessContext result = mock(TransmissionProcessContext.class);
         when(result.getProcessConfiguration()).thenReturn(new PipelineProcessConfiguration(new PipelineReadConfiguration(1, 10, 10, null),
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
index 571ba12d75d..58f82c40681 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
@@ -33,6 +33,7 @@ import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpServletResponseWrapper;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
+import org.apache.shardingsphere.infra.util.json.JsonEngine;
 import org.apache.shardingsphere.mcp.api.session.MCPSessionIdentity;
 import org.apache.shardingsphere.mcp.bootstrap.config.HttpTransportConfiguration;
 import org.apache.shardingsphere.mcp.bootstrap.transport.HttpTransportHostUtils;
@@ -72,8 +73,6 @@ final class StreamableHttpMCPServlet extends HttpServlet implements McpStreamabl
     
     private final HttpServletStreamableServerTransportProvider delegate;
     
-    private final McpJsonMapper jsonMapper;
-    
     private final ServerTransportSecurityValidator securityValidator;
     
     private final MCPSessionManager sessionManager;
@@ -92,7 +91,6 @@ final class StreamableHttpMCPServlet extends HttpServlet implements McpStreamabl
                 new OriginHeaderConstraint(HttpTransportHostUtils.isLoopbackHost(config.getBindHost())),
                 new ProtocolVersionHeaderConstraint(), sessionAttributionResolver);
         delegate = createDelegate(jsonMapper, config.getEndpointPath());
-        this.jsonMapper = jsonMapper;
         this.sessionManager = sessionManager;
         sessionExecutionCoordinator = new MCPSessionExecutionCoordinator(sessionManager);
         closed = new AtomicBoolean();
@@ -237,7 +235,7 @@ final class StreamableHttpMCPServlet extends HttpServlet implements McpStreamabl
         McpError error = MCPTransportErrorFactory.createError(cause);
         response.setStatus(cause.getStatusCode());
         response.setContentType(JSON_CONTENT_TYPE);
-        response.getWriter().write(jsonMapper.writeValueAsString(Map.of("jsonrpc", McpSchema.JSONRPC_VERSION, "error", error.getJsonRpcError())));
+        response.getWriter().write(JsonEngine.marshal(Map.of("jsonrpc", McpSchema.JSONRPC_VERSION, "error", error.getJsonRpcError())));
     }
     
     private Map<String, List<String>> extractHeaders(final HttpServletRequest request) {
diff --git a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServletTest.java b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServletTest.java
index d7df6b7e6db..96ee2e2a8c7 100644
--- a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServletTest.java
+++ b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServletTest.java
@@ -27,11 +27,14 @@ import io.modelcontextprotocol.spec.ProtocolVersions;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.apache.shardingsphere.infra.util.json.JsonEngine;
 import org.apache.shardingsphere.mcp.api.session.MCPSessionIdentity;
 import org.apache.shardingsphere.mcp.bootstrap.config.HttpTransportConfiguration;
 import org.apache.shardingsphere.mcp.bootstrap.config.SessionAttributionSourceConfiguration;
 import org.apache.shardingsphere.mcp.bootstrap.transport.MCPTransportConstants;
 import org.apache.shardingsphere.mcp.bootstrap.transport.MCPTransportJsonMapperFactory;
+import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator.MCPTransportSecurityException;
+import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator.ShardingSphereServerTransportSecurityValidator;
 import org.apache.shardingsphere.mcp.core.session.MCPSessionManager;
 import org.apache.shardingsphere.test.infra.framework.extension.log.LogCaptureAssertion;
 import org.apache.shardingsphere.test.infra.framework.extension.log.LogCaptureExtension;
@@ -49,10 +52,12 @@ import reactor.core.publisher.Mono;
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -69,6 +74,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyMap;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doThrow;
@@ -265,13 +271,29 @@ class StreamableHttpMCPServletTest {
         when(request.getHeaderNames()).thenReturn(Collections.enumeration(List.of("Origin")));
         when(request.getHeaders("Origin")).thenReturn(Collections.enumeration(List.of("https://example.com")));
         HttpServletResponse response = mock(HttpServletResponse.class);
-        when(response.getWriter()).thenReturn(mock(PrintWriter.class));
+        StringWriter actualResponseBody = new StringWriter();
+        when(response.getWriter()).thenReturn(new PrintWriter(actualResponseBody));
         createServlet(mock(MCPSessionManager.class)).service(request, response);
         verify(response).setStatus(HttpServletResponse.SC_FORBIDDEN);
+        verify(response).setContentType("application/json");
+        assertTransportSecurityError(actualResponseBody, "Origin is not allowed by MCP HTTP transport policy.");
         verify(response, never()).sendError(HttpServletResponse.SC_BAD_REQUEST, "Accept must include text/event-stream.");
         verify(getDelegate(), never()).service(any(HttpServletRequest.class), any(HttpServletResponse.class));
     }
     
+    private void assertTransportSecurityError(final StringWriter responseBody, final String expectedMessage) {
+        Map<?, ?> actual = JsonEngine.unmarshal(responseBody.toString(), Map.class);
+        assertThat(actual.keySet(), is(Set.of("jsonrpc", "error")));
+        assertThat(actual.get("jsonrpc"), is(McpSchema.JSONRPC_VERSION));
+        Map<?, ?> actualError = (Map<?, ?>) actual.get("error");
+        assertThat(actualError.keySet(), is(Set.of("code", "message", "data")));
+        assertThat(actualError.get("code"), is(McpSchema.ErrorCodes.INVALID_PARAMS));
+        assertThat(actualError.get("message"), is(expectedMessage));
+        Map<?, ?> actualData = (Map<?, ?>) actualError.get("data");
+        assertThat(actualData.get("summary"), is(expectedMessage));
+        assertThat(actualData.get("response_mode"), is("recovery"));
+    }
+    
     @Test
     void assertRejectUnsupportedEventStreamGet() throws ServletException, IOException {
         MCPSessionManager sessionManager = new MCPSessionManager(Map.of());
@@ -415,14 +437,37 @@ class StreamableHttpMCPServletTest {
         when(request.getHeaderNames()).thenReturn(Collections.enumeration(List.of("Origin")));
         when(request.getHeaders("Origin")).thenReturn(Collections.enumeration(List.of("https://example.com")));
         HttpServletResponse response = mock(HttpServletResponse.class);
-        when(response.getWriter()).thenReturn(mock(PrintWriter.class));
+        StringWriter actualResponseBody = new StringWriter();
+        when(response.getWriter()).thenReturn(new PrintWriter(actualResponseBody));
         createServlet(mock(MCPSessionManager.class)).service(request, response);
         verify(response).setStatus(HttpServletResponse.SC_FORBIDDEN);
+        verify(response).setContentType("application/json");
+        assertTransportSecurityError(actualResponseBody, "Origin is not allowed by MCP HTTP transport policy.");
         verify(request, never()).getContentType();
         verify(response, never()).sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "Content-Type must be application/json.");
         verify(getDelegate(), never()).service(any(HttpServletRequest.class), any(HttpServletResponse.class));
     }
     
+    @Test
+    void assertServiceTransportSecurityErrorEscaping() throws ServletException, IOException {
+        String expectedMessage = "foo_\"quoted\"\\path\nmessage";
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getMethod()).thenReturn("POST");
+        HttpServletResponse response = mock(HttpServletResponse.class);
+        StringWriter actualResponseBody = new StringWriter();
+        when(response.getWriter()).thenReturn(new PrintWriter(actualResponseBody));
+        try (
+                MockedConstruction<ShardingSphereServerTransportSecurityValidator> ignored = mockConstruction(ShardingSphereServerTransportSecurityValidator.class,
+                        (mock, context) -> doThrow(new MCPTransportSecurityException(403, expectedMessage, MCPTransportSecurityException.CATEGORY_ORIGIN_NOT_ALLOWED)).when(mock)
+                                .validateHeaders(anyMap()))) {
+            createServlet(mock(MCPSessionManager.class)).service(request, response);
+        }
+        verify(response).setStatus(HttpServletResponse.SC_FORBIDDEN);
+        verify(response).setContentType("application/json");
+        assertTransportSecurityError(actualResponseBody, expectedMessage);
+        verify(getDelegate(), never()).service(any(HttpServletRequest.class), any(HttpServletResponse.class));
+    }
+    
     @Test
     void assertServicePostWithNegotiatedProtocolHeader() throws ServletException, IOException {
         HttpServletRequest request = mock(HttpServletRequest.class);
diff --git a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/util/DatabaseExportMetaDataGeneratorTest.java b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/util/DatabaseExportMetaDataGeneratorTest.java
index b5ec7e28ee2..e55fbd10bcd 100644
--- a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/util/DatabaseExportMetaDataGeneratorTest.java
+++ b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/util/DatabaseExportMetaDataGeneratorTest.java
@@ -18,6 +18,7 @@
 package org.apache.shardingsphere.proxy.backend.util;
 
 import org.apache.shardingsphere.distsql.handler.executor.configuration.export.DatabaseExportMetaDataGenerator;
+import org.apache.shardingsphere.distsql.handler.executor.configuration.yaml.YamlDatabaseConfiguration;
 import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
 import org.apache.shardingsphere.infra.datasource.pool.props.domain.DataSourcePoolProperties;
 import org.apache.shardingsphere.infra.datasource.pool.props.domain.synonym.ConnectionPropertySynonyms;
@@ -26,8 +27,11 @@ import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
 import org.apache.shardingsphere.infra.metadata.database.resource.unit.StorageUnit;
 import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
+import org.apache.shardingsphere.infra.util.yaml.YamlEngine;
 import org.apache.shardingsphere.single.config.SingleRuleConfiguration;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -37,9 +41,12 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -73,6 +80,7 @@ class DatabaseExportMetaDataGeneratorTest {
         assertThat(actual, not(containsString("idleTimeout")));
         assertThat(actual, containsString("rules:"));
         assertThat(actual, containsString("defaultDataSource: write_ds"));
+        assertThat(YamlEngine.unmarshal(actual, YamlDatabaseConfiguration.class).getRules().size(), is(1));
     }
     
     private ShardingSphereDatabase mockDatabase() {
@@ -122,4 +130,71 @@ class DatabaseExportMetaDataGeneratorTest {
         result.setDefaultDataSource("write_ds");
         return result;
     }
+    
+    @ParameterizedTest(name = "{index}: {0}")
+    @ValueSource(strings = {"foo: bar # baz\n'quoted' \"value\" \\δΈ­", "null", "true", "00123"})
+    void assertGenerateYAMLFormatWithSpecialCharacters(final String value) {
+        ShardingSphereDatabase database = mockEmptyDatabase();
+        when(database.getName()).thenReturn(value);
+        Map<String, Object> expectedProps = new LinkedHashMap<>(5, 1F);
+        expectedProps.put("url", value);
+        expectedProps.put("username", value);
+        expectedProps.put("password", value);
+        expectedProps.put("maxPoolSize", 30);
+        expectedProps.put("readOnly", true);
+        Map<String, Object> props = new LinkedHashMap<>(expectedProps);
+        props.put("minPoolSize", null);
+        props.put("connectionInitSql", "SELECT 1");
+        Map<String, StorageUnit> storageUnits = mockStorageUnits(Collections.singletonMap(value, new DataSourcePoolProperties("com.zaxxer.hikari.HikariDataSource", props)));
+        when(database.getResourceMetaData().getStorageUnits()).thenReturn(storageUnits);
+        Map<?, ?> actual = YamlEngine.unmarshal(new DatabaseExportMetaDataGenerator(database).generateYAMLFormat(), Map.class);
+        assertThat(actual.get("databaseName"), is(value));
+        Map<?, ?> actualDataSources = (Map<?, ?>) actual.get("dataSources");
+        assertThat(actualDataSources.keySet(), contains(value));
+        assertThat(actualDataSources.get(value), is(expectedProps));
+    }
+    
+    @Test
+    void assertGenerateYAMLFormatWithDataSourceOrderAndPoolTypes() {
+        ShardingSphereDatabase database = mockEmptyDatabase();
+        Map<String, Object> props = new LinkedHashMap<>(4, 1F);
+        props.put("password", "");
+        props.put("maxPoolSize", 30);
+        props.put("connectionTimeoutMilliseconds", 30000L);
+        props.put("readOnly", false);
+        Map<String, DataSourcePoolProperties> dataSources = new LinkedHashMap<>(2, 1F);
+        dataSources.put("foo_ds", new DataSourcePoolProperties("com.zaxxer.hikari.HikariDataSource", props));
+        dataSources.put("bar_ds", createDataSourcePoolProperties());
+        Map<String, StorageUnit> storageUnits = mockStorageUnits(dataSources);
+        when(database.getResourceMetaData().getStorageUnits()).thenReturn(storageUnits);
+        String actualYaml = new DatabaseExportMetaDataGenerator(database).generateYAMLFormat();
+        Map<?, ?> actual = YamlEngine.unmarshal(actualYaml, Map.class);
+        assertThat(((Map<?, ?>) actual.get("dataSources")).keySet(), contains("foo_ds", "bar_ds"));
+        YamlDatabaseConfiguration actualConfig = YamlEngine.unmarshal(actualYaml, YamlDatabaseConfiguration.class);
+        assertThat(actualConfig.getDataSources().get("foo_ds").getPassword(), is(""));
+        assertThat(actualConfig.getDataSources().get("foo_ds").getMaxPoolSize(), is(30));
+        assertThat(actualConfig.getDataSources().get("foo_ds").getConnectionTimeoutMilliseconds(), is(30000L));
+        assertFalse(actualConfig.getDataSources().get("foo_ds").getReadOnly());
+    }
+    
+    @Test
+    void assertGenerateYAMLFormatWithEmptyDataSource() {
+        ShardingSphereDatabase database = mockEmptyDatabase();
+        Map<String, Object> props = new LinkedHashMap<>(1, 1F);
+        props.put("url", null);
+        Map<String, StorageUnit> storageUnits = mockStorageUnits(Collections.singletonMap("foo_ds", new DataSourcePoolProperties("com.zaxxer.hikari.HikariDataSource", props)));
+        when(database.getResourceMetaData().getStorageUnits()).thenReturn(storageUnits);
+        Map<?, ?> actual = YamlEngine.unmarshal(new DatabaseExportMetaDataGenerator(database).generateYAMLFormat(), Map.class);
+        Map<?, ?> actualDataSources = (Map<?, ?>) actual.get("dataSources");
+        assertThat(actualDataSources.keySet(), contains("foo_ds"));
+        assertNull(actualDataSources.get("foo_ds"));
+    }
+    
+    @Test
+    void assertGenerateYAMLFormatWithOnlyEmptyRules() {
+        ShardingSphereDatabase database = mockEmptyDatabase();
+        when(database.getRuleMetaData().getConfigurations()).thenReturn(Collections.singletonList(new SingleRuleConfiguration()));
+        Map<?, ?> actual = YamlEngine.unmarshal(new DatabaseExportMetaDataGenerator(database).generateYAMLFormat(), Map.class);
+        assertThat(actual.keySet(), contains("databaseName"));
+    }
 }
diff --git a/test/e2e/operation/pipeline/src/test/java/org/apache/shardingsphere/test/e2e/operation/pipeline/framework/helper/PipelineCaseHelper.java b/test/e2e/operation/pipeline/src/test/java/org/apache/shardingsphere/test/e2e/operation/pipeline/framework/helper/PipelineCaseHelper.java
index 2ff2fa76e28..f7f36ed44da 100644
--- a/test/e2e/operation/pipeline/src/test/java/org/apache/shardingsphere/test/e2e/operation/pipeline/framework/helper/PipelineCaseHelper.java
+++ b/test/e2e/operation/pipeline/src/test/java/org/apache/shardingsphere/test/e2e/operation/pipeline/framework/helper/PipelineCaseHelper.java
@@ -24,11 +24,13 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.shardingsphere.data.pipeline.cdc.util.RandomStrings;
 import org.apache.shardingsphere.infra.algorithm.core.context.AlgorithmSQLContext;
 import org.apache.shardingsphere.infra.algorithm.keygen.spi.KeyGenerateAlgorithm;
+import org.apache.shardingsphere.infra.util.json.JsonEngine;
 
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.time.LocalDateTime;
+import java.util.Collections;
 import java.util.concurrent.ThreadLocalRandom;
 
 import static org.mockito.Mockito.mock;
@@ -72,7 +74,7 @@ public final class PipelineCaseHelper {
         } else {
             value = generateString(length);
         }
-        return String.format("{\"test\":\"%s\"}", value);
+        return JsonEngine.marshal(Collections.singletonMap("test", value));
     }
     
     /**
diff --git a/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/strategy/type/GitHubTestParameterLoadStrategyTest.java b/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/strategy/type/GitHubTestParameterLoadStrategyTest.java
index a1c7346b8fc..497352df841 100644
--- a/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/strategy/type/GitHubTestParameterLoadStrategyTest.java
+++ b/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/strategy/type/GitHubTestParameterLoadStrategyTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.shardingsphere.test.it.sql.parser.external.loader.strategy.type;
 
+import org.apache.shardingsphere.infra.util.json.JsonEngine;
 import org.apache.shardingsphere.test.it.sql.parser.external.loader.summary.FileSummary;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -30,6 +31,9 @@ import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -50,9 +54,12 @@ class GitHubTestParameterLoadStrategyTest {
         URI sourceURI = URI.create("https://github.com/owner/repo/" + sourceType + "/" + ref + "/" + casePath);
         String apiURL = "https://api.github.com/repos/owner/repo/contents/" + casePath + "?ref=" + ref;
         String downloadURL = "https://raw.githubusercontent.com/owner/repo/" + ref + "/cases/sample.sql";
-        String fileContent = "{\"name\":\"sample.sql\",\"type\":\"file\",\"download_url\":\"" + downloadURL + "\"}";
+        Map<String, String> fileResponse = new LinkedHashMap<>(3, 1F);
+        fileResponse.put("name", "sample.sql");
+        fileResponse.put("type", "file");
+        fileResponse.put("download_url", downloadURL);
         try (MockedStatic<URI> uriMock = mockStatic(URI.class)) {
-            mockContent(uriMock, apiURL, "blob".equals(sourceType) ? fileContent : "[" + fileContent + "]");
+            mockContent(uriMock, apiURL, JsonEngine.marshal("blob".equals(sourceType) ? fileResponse : Collections.singletonList(fileResponse)));
             Collection<FileSummary> actual = new GitHubTestParameterLoadStrategy().loadSQLCaseFileSummaries(sourceURI);
             assertThat(actual.size(), is(1));
             FileSummary actualFileSummary = actual.iterator().next();
@@ -78,18 +85,28 @@ class GitHubTestParameterLoadStrategyTest {
     @Test
     void assertLoadSQLCaseFileSummariesRecursively() throws IOException {
         String ref = "f58ab18a90460cd297249bc80b13d84b2671bf0c";
-        URI sourceURI = URI.create("https://github.com/owner/repo/tree/" + ref + "/cases");
+        String downloadURL = "https://raw.githubusercontent.com/owner/repo/" + ref + "/cases/sample.sql";
+        Map<String, String> fileResponse = new LinkedHashMap<>(3, 1F);
+        fileResponse.put("name", "sample.sql");
+        fileResponse.put("type", "file");
+        fileResponse.put("download_url", downloadURL);
         String childURL = "https://github.com/owner/repo/tree/" + ref + "/cases/nested";
-        URI childURI = URI.create(childURL);
+        Map<String, String> directoryResponse = new LinkedHashMap<>(3, 1F);
+        directoryResponse.put("name", "nested");
+        directoryResponse.put("type", "dir");
+        directoryResponse.put("html_url", childURL);
+        String childDownloadURL = "https://raw.githubusercontent.com/owner/repo/" + ref + "/cases/nested/child.sql";
+        Map<String, String> childResponse = new LinkedHashMap<>(3, 1F);
+        childResponse.put("name", "child.sql");
+        childResponse.put("type", "file");
+        childResponse.put("download_url", childDownloadURL);
         String apiURL = "https://api.github.com/repos/owner/repo/contents/cases?ref=" + ref;
         String childApiURL = "https://api.github.com/repos/owner/repo/contents/cases/nested?ref=" + ref;
-        String downloadURL = "https://raw.githubusercontent.com/owner/repo/" + ref + "/cases/sample.sql";
-        String childDownloadURL = "https://raw.githubusercontent.com/owner/repo/" + ref + "/cases/nested/child.sql";
-        String content = "[{\"name\":\"sample.sql\",\"type\":\"file\",\"download_url\":\"" + downloadURL + "\"},{\"name\":\"nested\",\"type\":\"dir\",\"html_url\":\"" + childURL + "\"}]";
-        String childContent = "[{\"name\":\"child.sql\",\"type\":\"file\",\"download_url\":\"" + childDownloadURL + "\"}]";
+        URI sourceURI = URI.create("https://github.com/owner/repo/tree/" + ref + "/cases");
+        URI childURI = URI.create(childURL);
         try (MockedStatic<URI> uriMock = mockStatic(URI.class)) {
-            mockContent(uriMock, apiURL, content);
-            mockContent(uriMock, childApiURL, childContent);
+            mockContent(uriMock, apiURL, JsonEngine.marshal(Arrays.asList(fileResponse, directoryResponse)));
+            mockContent(uriMock, childApiURL, JsonEngine.marshal(Collections.singletonList(childResponse)));
             uriMock.when(() -> URI.create(childURL)).thenReturn(childURI);
             Collection<FileSummary> actual = new GitHubTestParameterLoadStrategy().loadSQLCaseFileSummaries(sourceURI);
             assertThat(actual.stream().map(FileSummary::getFileName).collect(Collectors.toList()), is(Arrays.asList("sample.sql", "child.sql")));