'XML data to Java record (array)

The error:

Caused by: java.lang.NullPointerException
at tinytool.models.TmsRecord.setNonCostType(TmsRecord.java:81)
at tinytool.CreateTimeSheet.loadData(CreateTimeSheet.java:74)
at tinytool.CreateTimeSheet.createTimeSheet(CreateTimeSheet.java:38)
at tinytool.controllers.TimesheetWindowController.handleGenerate(TimesheetWindowController.java:91)

As soon as I am trying to populate an array field (setNonCostNr) the NullPointerException is raised. Whenever a single element (customerName) is populated there are no problems at all.

This the XML record received from the mainframe

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<tmsRecord>
    <header>
        <customerName>THE CUSTOMER</customerName>
        <customerNr>999</customerNr>
        <personnelName>The Consultant</personnelName>
        <personnelNr>124578</personnelNr>
        <weekEnding>2022-02-26</weekEnding>
    </header>
    <nonBillable>
        <costType>K</costType>
        <costNr>E71500</costNr>
        <costItem></costItem>
        <taskLevel>V1</taskLevel>
        <taskType>V101</taskType>
        <taskComponent>HOURS</taskComponent>
        <comment></comment>
        <sunday></sunday>
        <monday></monday>
        <tuesday>0.5</tuesday>
        <wednesday></wednesday>
        <thursday></thursday>
        <friday></friday>
        <saturday></saturday>
        <total></total>
    </nonBillable>
    <nonBillable>
        <costType>K</costType>
        <costNr></costNr>
        <costItem></costItem>
        <taskLevel>V1</taskLevel>
        <taskType>V101</taskType>
        <taskComponent>HOURS</taskComponent>
        <comment></comment>
        <sunday></sunday>
        <monday></monday>
        <tuesday></tuesday>
        <wednesday></wednesday>
        <thursday>8.0</thursday>
        <friday></friday>
        <saturday></saturday>
        <total></total>
    <nonBillable>
</tmsrecord>

Record Class for creating a record entry with entries being an array of values, not only a single entry,

public class TmsRecord { 
    private String customerName;
    private String customerNr;
    private String personnelName;
    private String personnelNr;
    private String weekEnding;
    private String[] nonCostType;
    private String[] nonCostNr;
    private String[] nonCostItem;
    
    public void setCustomerName(String customerName) { 
        this.customerName = customerName; 
    }
    
    public void setCustomerNr(String customerNr) { 
        this.customerNr = customerNr; 
    }
        
    public void setPersonnelName(String personnelName) { 
        this.personnelName = personnelName; 
    }
        
    public void setPersonnelNr(String personnelNr) { 
        this.personnelNr = personnelNr; 
    }
        
    public void setWeekEnding(String weekEnding) { 
        this.weekEnding = weekEnding;  
    }
    
    public void setNonCostType(int pos, String costType) { 
        this.nonCostType[pos] = costType; 
    }
    
    public void setNonCostNr(int pos, String costNr) { 
        this.nonCostNr[pos] = costNr; 
    }
    
    public void setNonCostItem(int pos, String costItem) { 
        this.nonCostItem[pos] = costItem; 
    }
}

The class that creates the record from the XML file received from the mainframe

public class CreateTimeSheet {
    private final Utilities utilities = new Utilities();
    private SystemRecord systemRecord = new SystemRecord();
    private TmsRecord timeRecord;
    private boolean canContinue;
    private File userFile;
    
    public boolean createTimeSheet(File userFile, SystemRecord systemRecord, TmsRecord timeRecord) {
      this.userFile = userFile;
      this.systemRecord = systemRecord;
      this.timeRecord = timeRecord;
    
      canContinue = loadData();
      return canContinue;
    }
    
    private boolean loadData()  {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        Element element;
        NodeList list;
        Node node;
        double lineTotal;
    
        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document dom = builder.parse(userFile);
            timeRecord = new TmsRecord();
    
            list = dom.getElementsByTagName("header");
            node = list.item(0);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                element = (Element) node;
                timeRecord.setCustomerName(element.getElementsByTagName("customerName").item(0).getTextContent());
                timeRecord.setCustomerNr(element.getElementsByTagName("customerNr").item(0).getTextContent());
                timeRecord.setPersonnelName(element.getElementsByTagName("personnelName").item(0).getTextContent());
                timeRecord.setPersonnelNr(element.getElementsByTagName("personnelNr").item(0).getTextContent());
                timeRecord.setWeekEnding(element.getElementsByTagName("weekEnding").item(0).getTextContent());
            }
    
            list = dom.getElementsByTagName("nonBillable");
            for (int i=0; i<list.getLength(); i++) {
                node = list.item(i);
    
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    element = (Element) node;
                    timeRecord.setNonCostType(i, element.getElementsByTagName("costType").item(0).getTextContent());
                    timeRecord.setNonCostNr(i, element.getElementsByTagName("costNr").item(0).getTextContent());
                    timeRecord.setNonCostItem(i, element.getElementsByTagName("costItem").item(0).getTextContent());
                }
            }
        }
    }
}


Solution 1:[1]

By default, all non-primitive class members are implicitly initialized to null. For the String class members, you are assigning them a value, for example:

public void setCustomerName(String customerName) { 
    this.customerName = customerName; 
}

However you are not assigning a value to the array members. Hence calling the following method will throw NullPointerException since nonCostType is null.

public void setNonCostType(int pos, String costType) { 
    this.nonCostType[pos] = costType; 
}

You need to initialize the arrays before you try to assign values to the elements of the array. You can initialize the array when you declare it, for example:

private String[] nonCostType = new String[100];

This will create an array with 100 elements (which are initially all null, by the way). The problem with arrays is that you need to know the number of elements when you initialize them. If you don't know the number of elements then it is probably better to use a List, for example:

private List<String> nonCostType = new java.util.ArrayList<>();

You can just keep adding elements to a list.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Abra