data, String rootTagName) {
+ if (data == null) return null;
+ StringBuilder xml = new StringBuilder();
+ xml.append("\n");
+ appendTag(xml, rootTagName, data, "");
+ return xml.toString();
+ }
+
+ private void appendTag(StringBuilder sb, String tagName, Object value, String indent) {
+ // Extract pure tag name for closing tag (handle attributes in tagName like "Root attr='val'")
+ String pureTagName = tagName.split("\\s+")[0];
+
+ if (value == null) {
+ sb.append(indent).append("<").append(tagName).append("/>\n");
+ return;
+ }
+
+ if (value instanceof Map) {
+ sb.append(indent).append("<").append(tagName).append(">\n");
+ Map, ?> map = (Map, ?>) value;
+ for (Map.Entry, ?> entry : map.entrySet()) {
+ if (entry.getKey() != null) {
+ appendTag(sb, entry.getKey().toString(), entry.getValue(), indent + " ");
+ }
+ }
+ sb.append(indent).append("").append(pureTagName).append(">\n");
+ } else if (value instanceof List) {
+ List> list = (List>) value;
+ for (Object item : list) {
+ appendTag(sb, tagName, item, indent);
+ }
+ } else {
+ sb.append(indent).append("<").append(tagName).append(">");
+ String valStr = value.toString();
+ valStr = escapeXml(valStr);
+ sb.append(valStr);
+ sb.append("").append(pureTagName).append(">\n");
+ }
+ }
+
+ private String escapeXml(String str) {
+ if (str == null) return "";
+ return str.replace("&", "&")
+ .replace("<", "<")
+ .replace(">", ">")
+ .replace("\"", """)
+ .replace("'", "'");
+ }
+}
diff --git a/xml-generator-utils/src/main/java/com/yourcompany/xml/generator/utils/api/XmlEntityToXmlGeneratorService.java b/xml-generator-utils/src/main/java/com/yourcompany/xml/generator/utils/api/XmlEntityToXmlGeneratorService.java
new file mode 100644
index 000000000..23dea4495
--- /dev/null
+++ b/xml-generator-utils/src/main/java/com/yourcompany/xml/generator/utils/api/XmlEntityToXmlGeneratorService.java
@@ -0,0 +1,229 @@
+package com.yourcompany.xml.generator.utils.api;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.netease.lowcode.core.annotation.NaslLogic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 实体(JSON)转XML生成服务
+ *
+ * 将入参的实体JSON按指定类型反序列化后,基于实体字段值生成XML字符串。
+ * 生成规则与 {@link XmlGeneratorUtilsService#mapToXml(Map, List, String)} 保持一致:
+ * 支持XML声明、缩进、List重复标签、Map嵌套标签、以及在标签名包含属性时的闭合标签处理。
+ *
+ * 说明:由于平台侧入参/返回值不允许使用Object类型,且实体无法添加XML注解,本服务采用:
+ * JSON字符串 + Class<T> 的方式还原实体,并开启字段名大小写不敏感以增强兼容性。
+ */
+@Service
+public class XmlEntityToXmlGeneratorService {
+
+ private static final Logger LCAP_LOGGER = LoggerFactory.getLogger("LCAP_EXTENSION_LOGGER");
+
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ static {
+ OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ OBJECT_MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
+ }
+
+ /**
+ * 将实体JSON转换为XML字符串
+ *
+ * 入参作用:
+ *
+ * - entityJson:实体数据(JSON字符串)。用于在不暴露Object类型入参的前提下传递复杂结构。
+ * - clazz:实体类型。用于将JSON反序列化为目标实体,并支持字段名大小写不敏感匹配。
+ * - keyOrder:子节点顺序列表(可为空)。不为空时仅输出列表命中的字段,并按该顺序生成(只控制 根节点下一级 子标签顺序)。
+ * - keyOrderByTag:嵌套节点的子节点顺序配置(可为空)。Key为父节点标签名,Value为其子节点顺序。
+ * - rootTagName:根节点名称(必填)。可包含属性,例如 {@code Certificate xmlns="..."}。
+ *
+ *
+ * 大小写处理:
+ *
+ * - 反序列化实体时:字段名大小写不敏感。
+ * - 生成XML且指定keyOrder时:会对实体字段名做大小写不敏感匹配,但输出标签名以keyOrder中的key为准。
+ * - 生成XML且指定keyOrderByTag时:会对各层节点的字段名做大小写不敏感匹配,但输出标签名以配置中的key为准。
+ *
+ *
+ * @param entityJson 实体JSON字符串
+ * @param clazz 目标实体类型
+ * @param keyOrder XML子节点顺序列表(可为空)
+ * @param keyOrderByTag 嵌套节点的子节点顺序配置(可为空)
+ * @param rootTagName XML根节点名称(必填)
+ * @param 目标实体泛型类型
+ * @return 生成的XML字符串;当必填入参为空时返回null
+ */
+ @NaslLogic
+ public String entityJsonToXml(String entityJson, Class clazz, List keyOrder, Map> keyOrderByTag, String rootTagName) {
+ if (entityJson == null || entityJson.trim().isEmpty()) {
+ LCAP_LOGGER.error("输入实体 JSON 为空");
+ return null;
+ }
+ if (clazz == null) {
+ LCAP_LOGGER.error("实体类型 clazz 为空");
+ return null;
+ }
+ if (rootTagName == null || rootTagName.trim().isEmpty()) {
+ LCAP_LOGGER.error("根节点名称不能为空");
+ return null;
+ }
+
+ try {
+ T entity = OBJECT_MAPPER.readValue(entityJson, clazz);
+ Map dataMap = OBJECT_MAPPER.convertValue(entity, new TypeReference