如何读取指定结构文本文件并映射到Java对象后转YAML?
多Project场景下Java实体映射到YAML的错误修复
需求说明
逐行读取指定结构的文本文件,将内容填充到自定义的Parameter、Project等嵌套Java实体类中,最终转换为指定格式的YAML文件,禁止硬编码赋值。
待读取文本文件结构
- context:dev
- region:asia
- lob:all
- projects: (This is a list of project)
- name:hub
- topics: (List of Topic inside projects)
- dataType:0
- name:cdc
- plan:dev-rack
- dataType:0.dlq
- name:cdc
- plan:rack
- dataType:0
- name:raw
- plan:dev
- schemas:
- value.schema.file:-data-hub.raw.0-value-v1.json
- value.format:JSON
- dataType:Produce.dlq
- name:raw
- plan:ack
- schemas:
- value.schema.file:hub.raw.0-value-v1.json
- value.format:JSON
- dataType:0
- name:dom
- plan:dev
- schemas:
- value.schema.file:dom.0-value-v1.json
- value.format:JSON
- dataType:Produce.dlq
- name:dom
- plan:de
- schemas:
- value.schema.file:0-value-v1.json
- value.format:JSON
已定义Java实体类
Parameter类
@Getter @Setter @ToString public class Parameter { private String context; private String region; private String lob; private List<Project> projects; // 修正原字段名"Project"不符合Java命名规范问题 }
Project类
@Getter @Setter @ToString public class Project { private String name; private List<Topic> topics; }
Topic类
@Getter @Setter @ToString public class Topic { private String dataType; private String name; private String plan; private Schemas schemas; public boolean checkDlq(String dataType) { dataType = getDataType(); return dataType.contains("dlq"); } }
Schemas类
@Getter @Setter @ToString public class Schemas { private Value value; }
Value类
@Getter @Setter @ToString public class Value { private Schema schema; private String format; }
Schema类
@Getter @Setter @ToString public class Schema { private String file; }
目标YAML格式示例
context: "dev" region: "asia" lob: "all" projects: - name: "hub" topics: - dataType: "0" name: "cdc" plan: "devrack" - dataType: "0.dlq" name: "cdc" plan: "dev-bronze-rack" - dataType: "0" name: "raw" plan: "rack" schemas: value.schema.file: "hub.raw.0-value-v1.json" value.format: "JSON" - dataType: "Produce.dlq" name: "raw" plan: "devrack" schemas: value.schema.file: "raw.0-value-v1.json" value.format: "JSON" - dataType: "0" name: "dom" plan: "rack" schemas: value.schema.file: "0-value-v1.json" value.format: "JSON" - dataType: "0.dlq" name: "dom" plan: "dev" schemas: value.schema.file: "v1.json" value.format: "JSON"
问题描述
当前实现的YamlProcessor类在单个Project场景下可正常工作,但存在多个Project时,无法创建独立的Project列表,所有Topic会被添加到同一个Project的topics集合中。
现有问题代码
public class YamlProcessor { public void addToObject() throws IOException { Parameter parameter = new Parameter(); FileReader fileReader = new FileReader("C:/Users/zk72/Desktop/schema.txt"); BufferedReader bufferedReader = new BufferedReader(fileReader); String line = bufferedReader.readLine(); List<Topic> addTopic = new ArrayList<>(); List<Project> addProject = new ArrayList<>(); //Single Variable Map<String, String> singleVariable = new HashMap<>(); //For Project list - eg-> project1 , list of project Map<String, Map<String,String>> projects = new HashMap<>(); Map<String, Map<String,Map<String,String>>> ps = new HashMap<>(); //For Topics List - eg-> topic1, list of project Map<String, Map<String, String>> topics = new HashMap<>(); while (line != null) { String s[] = line.split(":"); switch (s[0]) { case Constants.CONTEXT: singleVariable.put(s[0],s[1]); break; case Constants.REGION: singleVariable.put(s[0],s[1]); break; case Constants.LOB: singleVariable.put(s[0],s[1]); break; case Constants.PROJECTS: Map<String, String> project = new HashMap<>(); Map<String, String> topic = new HashMap<>(); int projectCount = 1; while ((line = bufferedReader.readLine()) != null) { String ss[] = line.split(":"); if (ss[0].equals(Constants.NAME)) { if (!project.isEmpty() && project != null) { projects.put("project"+projectCount, project); projectCount++; project = new HashMap<>(); project.put(ss[0],ss[1]); }else { project.put(ss[0],ss[1]); } }else if (ss[0].equals(Constants.TOPICS)) { int topicCount = 1; while((line = bufferedReader.readLine()) != null){ String str[] = line.split(":"); if (str[0].equals(Constants.DATA_TYPE) && !str[0].equals(Constants.NAME)) { if(!topic.isEmpty() && topic !=null){ topics.put("topic"+topicCount, topic); topicCount++; topic = new HashMap<>(); topic.put(str[0],str[1]); } else { topic.put(str[0],str[1]); } } else { if(str.length > 1) topic.put(str[0],str[1]); } } topicCount++; topics.put("topic"+topicCount, topic); } } projectCount++; projects.put("project"+projectCount, project); } line = bufferedReader.readLine(); } parameter.setContext(singleVariable.get(Constants.CONTEXT)); parameter.setRegion(singleVariable.get(Constants.REGION)); parameter.setLob(singleVariable.get(Constants.LOB)); for (Map.Entry<String, Map<String,String>> entry: topics.entrySet()) { Topic topic = new Topic(); topic.setDataType(entry.getValue().get(Constants.DATA_TYPE)); topic.setName(entry.getValue().get(Constants.NAME)); topic.setPlan(entry.getValue().get(Constants.PLAN)); Schemas schemas = new Schemas(); Value value = new Value(); Schema schema = new Schema(); schema.setFile(entry.getValue().get(Constants.VALUE_SCHEMA_FILE)); value.setSchema(schema); value.setFormat(entry.getValue().get(Constants.VALUE_FORMAT)); schemas.setValue(value); topic.setSchemas(schemas); addTopic.add(topic); } for (Map.Entry<String, Map<String, String>> entry: projects.entrySet()) { Project project = new Project(); project.setName(entry.getValue().get(Constants.NAME)); project.setTopics(addTopic); addProject.add(project); } parameter.setProjects(addProject); System.out.println(parameter.toString()); DumperOptions options = new DumperOptions(); options.setIndent(2); options.setPrettyFlow(true); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); PrintWriter writer = new PrintWriter(new File("./src/main/resources/topology3.yml")); Yaml yaml = new Yaml(options); yaml.dump(parameter, writer); bufferedReader.close(); } public static void main(String[] args) throws IOException { YamlProcessor processor = new YamlProcessor(); processor.addToObject(); } }
解决方案
问题根源
- 全局
addTopic列表被所有Project共享,导致每个Project的topics集合都是同一批数据。 - 解析逻辑通过中间Map中转,没有直接维护Project与Topic的归属关系,无法区分Topic所属的Project。
修复后的代码实现
重构解析逻辑,直接操作实体类,在读取文本时实时维护Project和其对应的Topic列表:
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import java.io.BufferedReader; import java.io.FileReader; import java.io.File; import java.io.PrintWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class YamlProcessor { public void addToObject() throws IOException { Parameter parameter = new Parameter(); try (BufferedReader bufferedReader = new BufferedReader(new FileReader("C:/Users/zk72/Desktop/schema.txt"))) { String line; List<Project> projectList = new ArrayList<>(); Project currentProject = null; List<Topic> topicList = null; Topic currentTopic = null; Schemas currentSchemas = null; Value currentValue = null; while ((line = bufferedReader.readLine()) != null) { line = line.trim(); if (line.isEmpty()) { continue; } String[] parts = line.split(":", 2); if (parts.length < 2) { // 处理无值的标记行 switch (parts[0]) { case Constants.PROJECTS: projectList = new ArrayList<>(); break; case Constants.TOPICS: if (currentProject != null) { topicList = new ArrayList<>(); currentProject.setTopics(topicList); } break; case Constants.SCHEMAS: if (currentTopic != null) { currentSchemas = new Schemas(); currentValue = new Value(); currentSchemas.setValue(currentValue); currentTopic.setSchemas(currentSchemas); } break; } continue; } String key = parts[0].trim(); String value = parts[1].trim(); switch (key) { case Constants.CONTEXT: parameter.setContext(value); break; case Constants.REGION: parameter.setRegion(value); break; case Constants.LOB: parameter.setLob(value); break; case Constants.NAME: // 判断是Project还是Topic的名称 if (currentProject == null || (topicList != null && currentTopic != null)) { // 保存当前Project并创建新的 if (currentProject != null) { projectList.add(currentProject); } currentProject = new Project(); currentProject.setName(value); topicList = null; currentTopic = null; } else { // 创建新的Topic并加入当前Project的列表 currentTopic = new Topic(); currentTopic.setName(value); if (topicList != null) { topicList.add(currentTopic); } } break; case Constants.DATA_TYPE: if (currentTopic != null) { currentTopic.setDataType(value); } break; case Constants.PLAN: if (currentTopic != null) { currentTopic.setPlan(value); } break; case Constants.VALUE_SCHEMA_FILE: if (currentValue != null) { Schema schema = new Schema(); schema.setFile(value); currentValue.setSchema(schema); } break; case Constants.VALUE_FORMAT: if (currentValue != null) { currentValue.setFormat(value); } break; } } // 加入最后一个未保存的Project if (currentProject != null) { projectList.add(currentProject); } parameter.setProjects(projectList); // 输出到YAML文件 DumperOptions options = new DumperOptions(); options.setIndent(2); options.setPrettyFlow(true); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); try (PrintWriter writer = new PrintWriter(new File("./src/main/resources/topology3.yml"))) { Yaml yaml = new Yaml(options); yaml.dump(parameter, writer); } } } public static void main(String[] args) throws IOException { YamlProcessor processor = new YamlProcessor(); processor.addToObject(); } // 常量定义 public static class Constants { public static final String CONTEXT = "context"; public static final String REGION = "region"; public static final String LOB = "lob"; public static final String PROJECTS = "projects"; public static final String NAME = "name"; public static final String TOPICS = "topics"; public static final String DATA_TYPE = "dataType"; public static final String PLAN = "plan"; public static final String SCHEMAS = "schemas"; public static final String VALUE_SCHEMA_FILE = "value.schema.file"; public static final String VALUE_FORMAT = "value.format"; } }
修复说明
- 移除冗余中间Map,直接在读取文本时创建并维护实体对象的关联关系,确保Topic归属正确的Project。
- 通过
currentProject、currentTopic等变量跟踪当前解析的实体,明确层级关系。 - 处理无值标记行,正确触发实体层级的初始化。
- 修正Parameter类字段名的命名规范,避免序列化异常。
- 使用try-with-resources自动关闭流,提升代码健壮性。
内容的提问来源于stack exchange,提问作者Mukul Sharma
相关产品推荐
相关产品推荐

