Search code examples
javaxmlif-statementlogicxmlstreamreader

How to minimize the use of if else if in XMLStreamReader while parsing complex XML


I have the sample XML data below which I want to convert to a simple XML form.

I have to create multiple XML data based in the occurrence of certain element-value in the main XML data.

The logic is embedded inside a XMLStreamReader. While doing that I have to use multiple if else if and the code looks just messy. It seems like the if else if logic seems to grow if the xml tag contains grows.

  1. Is there a better design pattern to implement this logic?

  2. I have to convert the XML into Serializable Object, Is DOM better option here?

XML:

    <Bank createdTs="2014-11-26T16:50:13" xmlac = "http://www.trans.com/bank" xmlac:trans="http://www.trans.com/bank/dep/trans" xmlac:vref="http://www.trans.com/bank/security/verify">
<Transaction id="6f42cfee-ddd6-4d70-a6f7-a153d182c2b3" trans:type="deposit" trans:method="check">
    <UserRef status="Verify" vref:code="13" />
    <Account accountID="10002548" accountCategory="Checking">
        <TransactionRecord>
            <UserID>keith-kolmos</UserID>
            <Amount>4480</Amount>
            <LocationID>520</LocationID>
            <DateTimeStamp>2015-01-23T10:25:18</DateTimeStamp>
            <Comments>Check Verification Required</Comments>
        </TransactionRecord>
    </Account>
</Transaction>
<Transaction id="6f42cfee-ddd6-4d70-a6f7-a33d162c2b3" trans:type="withdraw" trans:method="cash">
    <Account accountID="10002548" accountCategory="Checking">
        <UserRef status="Complete" vref:code="10"/>
        <TransactionRecord>
            <UserID>zoe-danbur</UserID>
            <Amount>470</Amount>
            <LocationID>234</LocationID>
            <DateTimeStamp>2015-03-13T11:27:10</DateTimeStamp>
            <Comments/>
        </TransactionRecord>
    </Account>
</Transaction>
<Transaction id="6f42cfee-ddd6-4d70-a6f7-a0124d182c2b0" trans:type="deposit" trans:method="check">
    <Account accountID="10002548" accountCategory="Checking">
    <UserRef status="verify" vref:code="1"/>
        <TransactionRecord>
            <UserID>susan-wood</UserID>
            <Amount>585</Amount>
            <LocationID>127</LocationID>
            <DateTimeStamp>2015-03-25T09:20:32</DateTimeStamp>
            <Comments>Check Verified, photo ID presented</Comments>
        </TransactionRecord>
    </Account>
</Transaction>

XML DAO:

    public class BankDAO implements Serializable {
           private String accountID;
           private String accountCategory;
           private String transactionID;
           private String transactionType;
           private String transactionMethod;
           private String transactionStatus;
           private String transactionCode;
           private String userID;
           private String locationID;
           private String transactionAmount;
           private String dateTimeStamp;
           private String comments;

           //getters 
           //setters
           //toCustomXMLs
}

XML Parsing Logic Implementation:

public BankDAO parseXML(String xml) throws XMLStreamException, FactoryConfigurationError{
        XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(new ByteArrayInputStream(xml.getBytes()));

        BankDAO bank = new BankDAO();

        String currentElement = "";
        while (xmlReader.hasNext()){
            int code = xmlReader.next();
            switch(code){
            case START_ELEMENT:
                currentElement = xmlReader.getLocalName();
                break;
            case CHARACTERS:
                if (currentElement.equalsIgnoreCase("Transaction")) {
                    bank.setTransactionID(xmlReader.getAttributeValue("http://www.trans.com/bank/dep/trans","id"));
                    bank.setTransactionType(xmlReader.getAttributeValue("http://www.trans.com/bank/dep/trans","type"));
                    bank.setTransactionMethod(xmlReader.getAttributeValue("http://www.trans.com/bank/dep/trans","method"));
                } else if (currentElement.equalsIgnoreCase("UserRef")) {
                    bank.setTransactionStatus(xmlReader.getAttributeValue(null,"status"));
                    bank.setTransactionCode(xmlReader.getAttributeValue("http://www.trans.com/bank/security/verify","code"));
                }else if (currentElement.equalsIgnoreCase("Account")){
                    bank.setAccountID(xmlReader.getAttributeValue(null,"accountID"));
                    bank.setAccountCategory(xmlReader.getAttributeValue(null,"accountCategory"));
                }else if (currentElement.equalsIgnoreCase("UserID")){
                    bank.setAccountID(xmlReader.getAttributeValue(null,"UserID"));
                }else if (currentElement.equalsIgnoreCase("Amount")){
                    bank.setTransactionAmount(xmlReader.getElementText());
                }else if (currentElement.equalsIgnoreCase("LocationID")){
                    bank.setLocationID(xmlReader.getElementText());
                }else if (currentElement.equalsIgnoreCase("DateTimeStamp")){
                    bank.setDateTimeStamp(xmlReader.getElementText());
                }else if (currentElement.equalsIgnoreCase("Comments")){
                    bank.setComments(xmlReader.getElementText());
                }
            }
        }
        return bank;
    }

Solution

  • I would suggest you to use JAXB and XmlAdapter abstract class. All you need is in providing links. This will be good readable and flexible approach. Example:

    public class DateXmlAdapter extends XmlAdapter<String, Date> {
        public static final String dateFormat = "yyyy-MM-dd'T'HH:mm:ss";
    
        @Override
        public Date unmarshal(String v) throws Exception {
            return new SimpleDateFormat(dateFormat).parse(v);
        }
    
        @Override
        public String marshal(Date v) throws Exception {
            return v == null ? "" : new SimpleDateFormat(dateFormat).format(v);
        }
    }
    

    This adapter will convert date from String representation to java.util.Date. and vice versa. And JAXB object something like that:

    @XmlRootElement    
    @XmlAccessorType(XmlAccessType.FIELD)
    public class MyXMLObjectRepresentation {
         @XmlElement
         @XmlJavaTypeAdapter(DateXmlMigrateAdapter.class)
         private Date date;
    
         public Date getDate() {
             return date;
         }
    }
    

    You can start from this JAXB Hello World. Some hints. Your IDE probably(or you can install plugin if doesn't) can generate xsd files from xml. Then you can use XJC tool to generate java files.