Как я могу обрабатывать данные формата fixedLength? - PullRequest
0 голосов
/ 15 мая 2019

Я обрабатываю некоторые данные с фиксированной длиной.Поэтому я использую компонент bindy для обработки этих данных.

Этот файл содержит только одну запись.Запись имеет заголовок, несколько тел и нижний колонтитул.

  header record (total length : 20)
  1 : record_type (length : 1)
  VOLTE : service_type (length : 5)
  20190515 : creation date (length : 8)
  3 : custom_flag (length : 6)

  3 body records (total length : 20)
  2 : record_type (length : 1)
  01012345678 : mobile number (length : 11)
  20190515 : call start date (length : 8)

  footer records (total length : 20)
  3 : record_type (length : 1)
  AAAA.DAT : FILE NAME (length : 19)

Реальные данные

  1VOLTE20190515     32010123456782019051520101234567820190516201012345678201905173AAAA.DAT           

Я определил формат данных, как показано ниже.

Заголовок

  @FixedLengthRecord(length=20, paddingChar=' ')
  public class VoLTEHeader {

  @DataField(pos=1, length=1, trim=true)
  String record_type;

  @DataField(pos=2, length=5, trim=true)
  String service_type;

  @DataField(pos=7, length=8, trim=true)
  String creation_date;

  @DataField(pos=15, length=6, trim=true, align="R")
  String custom_flag;

Нижний колонтитул

  @FixedLengthRecord(length=20, paddingChar=' ')
  public class VoLTEFooter {

  @DataField(pos=1, length=1, trim=true)
  String record_type;

  @DataField(pos=2, length=19, trim=true)
  String file_name;

Тело

  @FixedLengthRecord(length=20, header=VoLTEHeader.class, footer=VoLTEFooter.class)
  public class VoLTEBody implements Serializable {

  @DataField(pos=1, length=1,trim=true)
  String record_type;

  @DataField(pos=2, length=11,trim=true)
  String mobile_number;

  @DataField(pos=13, length=8,trim=true)
  String call_start_date;

Я казнил верблюдамаршрут, но исключение произошло.

  java.lang.IllegalArgumentException: Size of the record: 100 is not equal to the value provided in the model: 20
  at org.apache.camel.dataformat.bindy.fixed.BindyFixedLengthDataFormat.createModel(BindyFixedLengthDataFormat.java:295) ~[camel-bindy-2.23.2.jar:2.23.2]
at org.apache.camel.dataformat.bindy.fixed.BindyFixedLengthDataFormat.unmarshal(BindyFixedLengthDataFormat.java:209) ~[camel-bindy-2.23.2.jar:2.23.2]
at org.apache.camel.processor.UnmarshalProcessor.process(UnmarshalProcessor.java:69) ~[camel-core-2.23.2.jar:2.23.2]
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548) [camel-core-2.23.2.jar:2.23.2]
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201) [camel-core-2.23.2.jar:2.23.2]
at org.apache.camel.processor.Pipeline.process(Pipeline.java:138) [camel-core-2.23.2.jar:2.23.2]
at org.apache.camel.processor.Pipeline.process(Pipeline.java:101) [camel-core-2.23.2.jar:2.23.2]

Я не думаю, что fixedLengthDataFormat обязательно нужно создавать в несколько строк.

Как я могу исправить эту проблему?

Ответы [ 2 ]

0 голосов
/ 16 мая 2019

Спасибо.Я реализовал это, хотя ваше руководство.Я думаю, что компонент Bindy поддерживает заголовок, нижний колонтитул и тело.

Итак, я рассмотрел ниже логику.

Входящий файл -> split (длина 20) -> агрегирующий файл split -> unmarshalling

Я использую агрегированный файл с верхним и нижним колонтитулами и телами.

Ниже приведен код.

  from("direct:start")
                    // assuming the raw record is in the body, we keep a copy in a Camel-header
                    .setHeader("record", body())
                    .setBody().groovy("request.body.split('(?<=\\\\G.{20})')")
                    .split(body(), AggregationStrategies.flexible().storeInHeader("bodyList").accumulateInCollection(ArrayList.class))
                    .unmarshal(bindyOneBody)
                    .end()
                    .log("VoLTEBody*: ${header.bodyList}")
                   ...

VoLTEBody

       @FixedLengthRecord(length=20, header=VoLTEHeader.class, footer=VoLTETailer.class)
       public class VoLTEBody {

       @DataField(pos=1, length=1,trim=true)
       String record_type;

Но есть ошибки, подобные приведенным ниже.

       Stacktrace
       ------------------------------------------------------------------------------ 
       --------------------------------------------------------- 
       java.lang.IllegalArgumentException: No records have been defined in the file
               at org.apache.camel.dataformat.bindy.fixed.BindyFixedLengthDataFormat.unmarshal(BindyFixedLengthDataFormat.java:250) ~[camel-bindy-2.23.2.jar:2.23.2]
               at org.apache.camel.processor.UnmarshalProcessor.process(UnmarshalProcessor.java:69) ~[camel-core-2.23.2.jar:2.23.2]
               at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548) [camel-core-2.23.2.jar:2.23.2]
               at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201) [camel-core-2.23.2.jar:2.23.2]
               at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548) [camel-core-2.23.2.jar:2.23.2]

Я думаю, что это нормально, если аннотация о верхнем и нижнем колонтитуле правильно определена в классе VoLTEBody.

Как я могу решить эту проблему?

Кроме того, я выполнил еще один тест.В этом случае разделение отсутствует.

Входящий файл -> unmarshalling (bindyOneBody)

Маршрутизатор

    from("direct:start")
                    // assuming the raw record is in the body, we keep a copy in a Camel-header
                    .setHeader("record", body())
                    //.setBody().groovy("request.body.split('(?<=\\\\G.{20})')")
                    //.split(body(), AggregationStrategies.flexible().storeInHeader("bodyList").accumulateInCollection(ArrayList.class))
                    .unmarshal(bindyOneBody)

Результат ниже.В результате мы не можем найти структуры VoLTEHeader и VoLTEFooter.Это нормально.

     2019-05-16 15:22:15,798 DEBUG BindyFixedLengthDataFormat - Graph of objects created: {camel.dataformat.volte.VoLTEBody_sample=VoLTEBody[record_type=2, mobile_number=01012345678, call_start_date=20190517]} 
     2019-05-16 15:22:15,798 INFO  route1 - [VoLTEBody[record_type=2, mobile_number=01012345678, call_start_date=20190515], VoLTEBody[record_type=2, mobile_number=01012345678, call_start_date=20190516], VoLTEBody[record_type=2, mobile_number=01012345678, call_start_date=20190517]] 
     2019-05-16 15:22:15,798 INFO  MyRouteBuilderTest - ******************************************************************************** 
0 голосов
/ 15 мая 2019

Camel bindy определяет заголовок как " отдельную запись заголовка в начале файла / потока " и нижний колонтитул как " одну запись нижнего колонтитула в конце файла / потока", см. [Документ о верблюде-бинди] [1].

Ваши тестовые данные содержат метаданные до и после части с несколькими полезными нагрузками в одной строке, вы не можете использовать модели верхнего и нижнего колонтитула Bindy для анализа этого.

Вместо этого создайте независимые модели Bindy для заголовка, нижнего колонтитула и одного тела (по существу удаляя заголовок = VoLTEHeader.class, footer = VoLTEFooter.class "из VoLTEBody) и индивидуально обработайте заголовок, нижний колонтитул и тела:

public class MyRouteBuilderTest extends CamelTestSupport {
    @Produce(uri="direct:start")
    private ProducerTemplate producer;

    @Override
    protected RouteBuilder createRouteBuilder() {
        return new RouteBuilder() {
            public void configure() {
                DataFormat bindyHeader = new BindyFixedLengthDataFormat(VoLTEHeader.class);
                DataFormat bindyFooter = new BindyFixedLengthDataFormat(VoLTEFooter.class);
                DataFormat bindyOneBody = new BindyFixedLengthDataFormat(VoLTEBody.class);

                from("direct:start")
                    // assuming the raw record is in the body, we keep a copy in a Camel-header
                    .setHeader("record", body())

                    // get the VoLTE header string into the Camel-body and unmarshal, then put VoLTE header object into Camel-header for later use
                    .setBody().groovy("request.body.substring(0,20)")
                    .unmarshal(bindyHeader)
                    .setHeader("header", body())

                    // restore VoLTE record string to Camel-body, get the VoLTE footer string into the Camel-body and unmarshal, then put footer VoLTE object into Camel-header for later use
                    .setBody().header("record")
                    .setBody().groovy("request.body.substring(request.body.length()-20,request.body.length())")
                    .unmarshal(bindyFooter)
                    .setHeader("footer", body())

                    // restore VoLTE record string to Camel-body, get the multi-bodies string into the Camel-body
                    .setBody().header("record")
                    .setBody().groovy("request.body.substring(20,request.body.length()-20)")

                    // Split VoLTE bodies string to each 20 char length, unmarshal each and finally put the list of VoLTE body objects into a Camel-header
                    .setBody().groovy("request.body.split('(?<=\\\\G.{20})')")
                    .split(body(), AggregationStrategies.flexible().storeInHeader("bodyList").accumulateInCollection(ArrayList.class))
                        .unmarshal(bindyOneBody)
                    .end()

                    // now do something with the unmarshalled objects in Camel-headers "header" (type VoLTEHeader), "footer" (type VoLTEFooter) and "bodyList" (type List<VoLTEBody>)
                    .log("VoLTEHeader: ${header.header}")
                    .log("VoLTEBody*: ${header.bodyList}")
                    .log("VoLTEFooter: ${header.footer}")
                    ;
            }
        };
    }

    @Test
    public void test() throws Exception {
        producer.sendBody("1VOLTE20190515     32010123456782019051520101234567820190516201012345678201905173AAAA.DAT           ");
    }
}

@FixedLengthRecord(length = 20, paddingChar = ' ')
public class VoLTEHeader {
    @DataField(pos = 1, length = 1, trim = true)
    String record_type;

    @DataField(pos = 2, length = 5, trim = true)
    String service_type;

    @DataField(pos = 7, length = 8, trim = true)
    String creation_date;

    @DataField(pos = 15, length = 6, trim = true, align = "R")
    String custom_flag;

    @Override
    public String toString() {
        return String.format("VoLTEHeader[record_type=%s, service_type=%s, creation_date=%s, custom_flag=%s]", record_type, service_type, creation_date, custom_flag);
    }
}

@FixedLengthRecord(length = 20)
public class VoLTEBody {
    @DataField(pos = 1, length = 1, trim = true)
    String record_type;

    @DataField(pos = 2, length = 11, trim = true)
    String mobile_number;

    @DataField(pos = 13, length = 8, trim = true)
    String call_start_date;

    @Override
    public String toString() {
        return String.format("VoLTEBody[record_type=%s, mobile_number=%s, call_start_date=%s]", record_type, mobile_number, call_start_date);
    }
}

@FixedLengthRecord(length = 20, paddingChar = ' ')
public class VoLTEFooter {
    @DataField(pos = 1, length = 1, trim = true)
    String record_type;

    @DataField(pos = 2, length = 19, trim = true)
    String file_name;

    @Override
    public String toString() {
        return String.format("VoLTEFooter[record_type=%s, file_name=%s]", record_type, file_name);
    }
}

Выход:

[main] INFO route1 - VoLTEHeader: VoLTEHeader[record_type=1, service_type=VOLTE, creation_date=20190515, custom_flag=3]
[main] INFO route1 - VoLTEBody*: [VoLTEBody[record_type=2, mobile_number=01012345678, call_start_date=20190515], VoLTEBody[record_type=2, mobile_number=01012345678, call_start_date=20190516], VoLTEBody[record_type=2, mobile_number=01012345678, call_start_date=20190517]]
[main] INFO route1 - VoLTEFooter: VoLTEFooter[record_type=3, file_name=AAAA.DAT           ]

В конце маршрута у вас должен быть объект типа VoLTEHeader в заголовке Camel-заголовка, объект типа VoLTEFooter в заголовке Camel-заголовка и список VoLTEBody в заголовке Camel-bodyList , Теперь вы можете обработать их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...