Search code examples
javabean-io

BeanIo nested list fixedlength marshal and unmarshal


I can't figure out why nested object aren't clearly parsed from BeanIo.

The parsing is based on "fixedlength", so positional.

Give these pojos:

@Record(minOccurs=1) 
@Data
public class Address {
  
  @Field(length=2)//,at=1)
  private String prov;
  @Field(length=6)//at=2)
  private String city; 
}

and

@Record(minOccurs=1) 
@Data
public class Employee { 
    @Field(length=6)//,at=1)
    private String firstName; 
    private List<Address> addresses; 
    @Field(length=6)//at=2)
    private String lastName; 
}

I've made this class to attempt both input and output operations to clearify the problem:

public class BeanioTest {

    public static void main(String[] args) {
        Employee emp = new Employee();
        emp.setFirstName("Joe");
        emp.setLastName("Black");

        List<Address> addresses = new ArrayList<>();

        Address address1 = new Address();

        address1.setCity("PARIS");
        addresses.add(address1);

        Address address2 = new Address();

        address2.setCity("MILAN");
        addresses.add(address2);

        emp.setAddresses(addresses);
        String marshalled = marshaller(  emp);
        System.out.println(marshalled);


        Employee empUnm = unmarshaller(marshalled);

        System.out.println(empUnm.toString());
    } 

    public static Employee unmarshaller(String emp) {
        StreamFactory factory = StreamFactory.newInstance();

        StreamBuilder builder = new StreamBuilder("s1")
                .format("fixedlength") 
                .addRecord(Employee.class)
                .addRecord(Address.class);

        factory.define(builder);

        Unmarshaller unmarshaller = factory.createUnmarshaller("s1"); 
        Employee unmarshalled = (Employee) unmarshaller.unmarshal(emp); 
        return unmarshalled;     

    }

    public static String marshaller(Employee emp) {

        StreamFactory factory = StreamFactory.newInstance();

        StreamBuilder builder = new StreamBuilder("Tm")
                .format("fixedlength") 
                .addRecord(Employee.class)
                .addRecord(Address.class);

        factory.define(builder);

        Marshaller marshaller = factory.createMarshaller("Tm"); 
        String marshalled = marshaller.marshal(emp).toString(); 
        return marshalled;   

    }
}

Bu the input and output won't take care of nested colletion, console output looks like this:

Joe   Black 
Employee(firstName=Joe, addresses=null, lastName=Black)

I've tested various attempt but can't figure out. Pls point me in the right direction.


Solution

  • Please also refer to the Segments section of the BeanIO Reference Guide. It is too much to copy here, but a segment is used to indicate nested objects inside a @Record. To make use of the segments you need to annotate the property List<Address> with the @Segment annotation and specify the collection type in this case. Your Employee class then becomes:

    @Data
    @Record(minOccurs = 1)
    @SuppressWarnings("javadoc")
    public class Employee {
    
      @Field(length = 6)// ,at=1)
      private String firstName;
      @Segment(collection = List.class)
      private List<Address> addresses;
      @Field(length = 6)// at=2)
      private String lastName;
    }
    

    There are various other attributes you can configure for a segment. See the documentation for more options.

    PS: Not part of the problem, but you probably have a typo in your test code:

      Address address2 = new Address();
      address1.setCity("MILAN"); // I assume you want MILAN to be the city for address2, not address1
      addresses.add(address2);