【问题标题】:Consume JSON from another API using Apache Camel and write it in CSV使用 Apache Camel 从另一个 API 使用 JSON 并将其写入 CSV
【发布时间】:2020-09-09 13:55:56
【问题描述】:

我是 apache 骆驼的新手。我想从另一个 api 的端点 url (http://localhost:8080/employeePayload) 使用 json 数据。我想使用这个 json 数据,使用 apache camel 将其解组到 POJO 并将其写入 CSV 文件。在我用 CSV 编写它之前,我需要为每个属性/列添加分隔符 |"。 此外,我必须将标题和尾部记录添加到 CSV 文件中。标头由一些静态列名和一些运行时名称组成。标题列数将与 json 数据不同。 Header 有 15 列,json 有 110 列)。

有没有办法在不使用 setter 方法的情况下将 json/pojo 绑定到 CSV 文件(因为我有 110 个属性,不能全部使用 setter)?

有人可以建议实现这一目标的最佳方法吗?

如果标题部分不清楚,请告诉我如何使用分隔符(|") 将 json 写入 CSV。

编辑:我可以从另一个 API 使用 json。但是,我不确定如何使用 apache camel 或任何其他库在不使用 getter 和 setter 的情况下使用分隔符将其写入 CSV。

【问题讨论】:

  • >有没有办法在不使用 setter 方法的情况下将 json/pojo 绑定到 CSV 文件(因为我有 110 个属性,不能全部使用 setter)?如果你想使用setter,你可以使用lombok,它只是一个类级别的注释@Data,以便为你的所有属性生成getter和setter。
  • 我的意思是有一种方法可以使用 apache camel 写入 CSV 而不使用 getter 方法。我正在使用 @Data 注释来避免为 pojo 创建 getter setter。然而,为了用 CSV 编写它,我们仍然必须使用 getter。
  • 您可以对 Java CSV 库进行一些研究。一个好的库会让你传入一个对象,它会为你处理调用 getter。我认为 Camel 在后台使用 Apache Commons CSV,所以如果它已经不能做到这一点,我会感到惊讶。
  • 是的。我找到了在 apache camel 的 BindyCsvDataFormat 类中不使用 getter 和 setter 来映射 CSV 的解决方案。我正在尝试在旅途中添加标题和预告片。

标签: java spring-boot apache-camel opencsv apache-commons-csv


【解决方案1】:

我找到了答案。 随着开发的进展,我有很多问题,我已经将它们发布在 stackoverflow 中。

我决定总结一下我发现的一切。

请参考以下帖子。 Unable to add header and footer to the file using Apache Camel Bindy

这个解决方案解决了我在各个阶段遇到的许多问题。

问题 1) 使用 BindyFixedLengthDataFormat 为平面文件添加了动态页眉和页脚。顾名思义,它是固定长度的。在生成正文后,我必须指定最大长度并修剪空格。 (我仍然可能需要考虑使用不同的类,因为我不希望长度固定)。 BindyCsvDataFormat 没有向文件添加动态页眉和页脚的选项。

问题 2) 我需要用 |" 替换管道分隔符。

问题 3)我没有使用 jetty 公开将被硬编码的端点 url,而是使用了 rest 配置,这样主机名将不再被硬编码,您可以部署到任何环境。

@Component
    public class EmployeePackageRoute extends RouteBuilder {
    
        @Autowired
        private Header header;
        
        private Trailer footer;
        
        private String file_name;
        
        @SuppressWarnings("deprecation")
        @Override
        public final void configure() {
            
            System.out.println("fetching employee details from employee api");
            
            final SimpleDateFormat TARGET_DATE_FORMAT = new SimpleDateFormat("YYYYMMDDHHMMSS");
            String date = TARGET_DATE_FORMAT.format(new Date());
            
            String year = date.substring(0, 8);
            String hr = date.substring(9, 13);
    
            try {
                restConfiguration().component("jetty").port(8000).bindingMode(RestBindingMode.json);
                rest("/api/test/getTestResponse")
                .get().consumes(MediaType.APPLICATION_JSON_VALUE).route().setBody().constant("return test response");
                
                rest("/api/bcr/recurringPaymentsDeduct")
                .post().consumes(MediaType.APPLICATION_JSON_VALUE).type(RecurringPaymentResults.class).outType(ResponseEntity.class).route()
                .process(
                        ex -> {
                            
                            RecurringPaymentResults result = ex.getIn().getBody(RecurringPaymentResults.class);
                            List<RecurringPaymentsDeduct> employee = result.getResults();
                            file_name = Constants.FILE_NUMBER_DEV+"_"+date+"_"+Constants.AUMBCR_HRMD+result.getSequenceNumber()+"_"+Constants.DUT8G2I+".SAP";
                            header.setHeader7(Constants.FILE_NUMBER_DEV+"_"+date+"_"+Constants.AUMBCR_HRMD+result.getSequenceNumber()+"_"+Constants.DUT8G2I+".SAP");
                            
                            header.setHeader8(year);
                            header.setHeader9(hr);
                            header.setHeader10(Constants.ENVIRONMENT);
                            
                            footer = new Footer("Footer",(employee.size()+2));
                            
                            ex.getOut().setBody(employee);
                        })
                .to("seda:recurringPaymentsFeed");
                
                final DataFormat recurringPaymentsBindy = new BindyFixedLengthDataFormat(RecurringPaymentsDeduct.class);
                
                from("seda:recurringPaymentsFeed")
                .startupOrder(2)
                .log("add footer to the file")
                .process(
                        ex -> {
                            
                            Map<String, Object> headerObjMap = new HashMap<String,Object>();
                            headerObjMap.put(Header.class.getName(), header);
                            System.out.println(" *** file_name 1 - "+file_name);
                            Map<String, Object> footerObjMap = new HashMap<String,Object>();
                            footerObjMap.put(Footer.class.getName(), footer);
                            
                            ex.getOut().setHeader(BindyFixedLengthDataFormat.CAMEL_BINDY_FIXED_LENGTH_HEADER, headerObjMap);
                            ex.getOut().setBody(ex.getIn().getBody());
                            ex.getOut().setHeader(BindyFixedLengthDataFormat.CAMEL_BINDY_FIXED_LENGTH_FOOTER, footerObjMap);
                            
                        })
                .marshal(recurringPaymentsBindy)
                .convertBodyTo(byte[].class, "iso-8859-1")
                .setBody(body().regexReplaceAll("\\|", "\\|\""))
                .log("csv processed")
                .to("file://app/bcr-files?fileName=recurringPaymentsDeduct.SAP")
                .end();
                
                
            } catch(Exception e) {
                System.out.println("Error occurred while processing employee data: "+e.getMessage());
                e.printStackTrace();
            }
        }
        
    }

@Data
@Section(number = 2)
@FixedLengthRecord(header = Header.class, footer = Trailer.class)
public class RecurringPaymentsDeduct implements Serializable {

    private static final long serialVersionUID = 1L;
    
    @DataField(pos = 1, length = 10, delimiter = "|", trim = true)
    public String RECTY;
    
    @DataField(pos = 2, length = 10, delimiter = "|", trim = true)
    public String CLIID;
    
    @DataField(pos = 3, length = 10, delimiter = "|", trim = true)
    public String INTCA;
    
    @DataField(pos = 4, length = 10, delimiter = "|", trim = true)
    public String ORDNO;
    
    @DataField(pos = 5, length = 10, delimiter = "|", trim = true)
    public String IOPER;
    
    @DataField(pos = 6, length = 10, delimiter = "|", trim = true)
    public String INFTY;
    
    @DataField(pos = 7, length = 10, delimiter = "|", trim = true)
    public String SUBTY;
    
    @DataField(pos = 8, length = 10, delimiter = "|", trim = true)
    public String BEGDA;
    
    @DataField(pos = 9, length = 10, delimiter = "|", trim = true)
    public String ENDDA;
    
    @DataField(pos = 10, length = 10, trim = true)
    public String OBJPS;
        
}

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties("adp")
@Data
@Section(number = 1)
@FixedLengthRecord()
public class Header implements Serializable {

    private static final long serialVersionUID = 1L;

    @DataField(pos = 1, length = 5, delimiter = "|", trim = true)
    private String header1;
    
    @DataField(pos = 2, length = 5, delimiter = "|", trim = true)
    private String header2;
    
    @DataField(pos = 3, length = 15, delimiter = "|", trim = true)
    private String header3;
    
    @DataField(pos = 4, length = 60, delimiter = "|", trim = true)
    private String header4;
    
    @DataField(pos = 5, length = 15, delimiter = "|", trim = true)
    private String header5;
    
    @DataField(pos = 6, length = 30, delimiter = "|", trim = true)
    private String header6;
    
    @DataField(pos = 7, length = 30, delimiter = "|", trim = true)
    private String header7;
    
    @DataField(pos = 8, length =  8, pattern = "YYYYMMDD", delimiter = "|", trim = true)
    private String header8;
    
    @DataField(pos = 9, length = 6, delimiter = "|", trim = true)
    private String header9;
    
    @DataField(pos = 10, length = 1, delimiter = "|", trim = true)
    private String header10;
    
    @DataField(pos = 11, length = 2, delimiter = "|", trim = true)
    private String header11;
    
    @DataField(pos = 12, length = 10, delimiter = "|", trim = true)
    private String header12;
    
    @DataField(pos = 13, length = 10, delimiter = "|", trim = true)
    private String header13;
    
    @DataField(pos = 14, length = 10, align = "L", trim = true)
    private String header14;
}

@Data
@Section(number = 3)
@FixedLengthRecord()
public class Footer implements Serializable {

    private static final long serialVersionUID = 1L;

    @DataField(pos = 1, length = 7, delimiter = "|", trim = true)
    private String trailer;
    
    @DataField(pos = 2, length = 5, align = "L", trim = true)
    private int count;
    
    public Footer() {
        
    }
    
    public Footer(String trailer, int count) {
        this.trailer = trailer;
        this.count = count;
    }
}

【讨论】:

  • 感谢分享您的解决方案。我建议你不要使用SimpleDateFormatDate。这些类设计不良且过时,尤其是前者,尤其是出了名的麻烦。而是使用来自java.time, the modern Java date and time APILocalDateTimeDateTimeFormatter。此外,您的格式模式字符串 YYYYMMDDHHMMSS 是错误的。它是区分大小写的,所以你需要检查哪些字母应该是大写,哪些是小写。
  • 感谢您强调,是的,我同意 SimpleDateFormat 不好用。它没有给出该模式的确切日期。我只是从以前的来源复制粘贴,并没有费心去改变它,因为它正在测试。不过,我会在我的项目中使用 LocalDateTime。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-05
  • 2019-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多