4
\$\begingroup\$

I need to deserialize an XML file, without using any sort of "reflection" (or meta-programming). Instead, I need to do it via a raw object, that knows of it's structure and can properly identify and deserialize itself.

In this case, I have an XML file that looks like the following:

<Test>
    <Item>
        <Group>
            <SubGroup test4="2018-09-01" test5="Y">

            </SubGroup>
        </Group>        
    </Item>
    <Item>
        <Group>
            <SubGroup test="0" test2="t">

            </SubGroup>
            <SubGroup test3="0.0">

            </SubGroup>
        </Group>
    </Item>
</Test>

And I have the following serialization classes:

UtilityMethods.java:

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

public class UtilityMethods {
    public static byte getInt8(String input) {
        if (input == null || input.equals("")) {
            return 0;
        } else {
            return Byte.parseByte(input);
        }
    }
    public static short getInt16(String input) {
        if (input == null || input.equals("")) {
            return 0;
        } else {
            return Short.parseShort(input);
        }
    }
    public static int getInt32(String input) {
        if (input == null || input.equals("")) {
            return 0;
        } else {
            return Integer.parseInt(input);
        }
    }
    public static long getInt64(String input) {
        if (input == null || input.equals("")) {
            return 0L;
        } else {
            return Long.parseLong(input);
        }
    }
    public static Date getDate(String input) throws ParseException {
        if (input == null || input.equals("")) {
            return new Date();
        } else {
            return new SimpleDateFormat("yyyy-MM-dd").parse(input);
        }
    }
    public static double getDouble(String input) {
        if (input == null || input.equals("")) {
            return 0.0;
        } else {
            return Double.parseDouble(input);
        }
    }
    public static boolean getBoolean(String input) {
        if (input == null || input.equals("")) {
            return false;
        } else {
            return (input.equals("y") ? true : input.equals("yes") ? true : false);
        }
    }

    public static ArrayList<Node> getNodesByTagName(Node rootNode, String tagName) {
        ArrayList<Node> result = new ArrayList<>();
        NodeList subNodes = rootNode.getChildNodes();

        for (int i = 0; i < subNodes.getLength(); i++) {
            if (subNodes.item(i).getNodeName().equals(tagName)) {
                result.add(subNodes.item(i));
            }
        }

        return result;
    }

    public static String getAttrValue(Node attr) {
        if (attr == null) {
            return null;
        } else {
            return attr.getNodeValue();
        }
    }
}

SubGroup.java:

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;

public class SubGroup {
    private Date _test4;
    public Date get_test4() { return _test4; }
    public void set_test4(Date test4) { _test4 = test4; }

    private boolean _test5;
    public boolean get_test5() { return _test5; }
    public void set_test5(boolean test5) { _test5 = test5; }

    private String __Text;
    public String get__Text() { return __Text; }
    public void set__Text(String _Text) { __Text = _Text; }

    private String _test2;
    public String get_test2() { return _test2; }
    public void set_test2(String test2) { _test2 = test2; }

    private byte _test;
    public byte get_test() { return _test; }
    public void set_test(byte test) { _test = test; }

    private double _test3;
    public double get_test3() { return _test3; }
    public void set_test3(double test3) { _test3 = test3; }

    public void deserialize(String xml) throws ParserConfigurationException, IOException, SAXException, ParseException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
        Document doc = documentBuilder.parse(new ByteArrayInputStream(xml.getBytes("utf-8")));
        Element root = doc.getDocumentElement();
        deserialize(doc.getElementsByTagName("SubGroup").item(0));
    }

    public void deserialize(Node myNode) throws ParseException {
        NodeList nodes;
        _test4 = UtilityMethods.getDate(UtilityMethods.getAttrValue(myNode.getAttributes().getNamedItem("test4")));
        _test5 = UtilityMethods.getBoolean(UtilityMethods.getAttrValue(myNode.getAttributes().getNamedItem("test5")));
        __Text = myNode.getTextContent();
        _test2 = UtilityMethods.getAttrValue(myNode.getAttributes().getNamedItem("test2"));
        _test = UtilityMethods.getInt8(UtilityMethods.getAttrValue(myNode.getAttributes().getNamedItem("test")));
        _test3 = UtilityMethods.getDouble(UtilityMethods.getAttrValue(myNode.getAttributes().getNamedItem("test3")));
    }
}

Group.java:

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;

public class Group {
    private ArrayList<SubGroup> _subGroup;
    public ArrayList<SubGroup> get_subGroup() { return _subGroup; }
    public void set_subGroup(ArrayList<SubGroup> subGroup) { _subGroup = subGroup; }

    public void deserialize(String xml) throws ParserConfigurationException, IOException, SAXException, ParseException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
        Document doc = documentBuilder.parse(new ByteArrayInputStream(xml.getBytes("utf-8")));
        Element root = doc.getDocumentElement();
        deserialize(doc.getElementsByTagName("Group").item(0));
    }

    public void deserialize(Node myNode) throws ParseException {
        NodeList nodes;
        _subGroup = new ArrayList<>();
        for (Node node : UtilityMethods.getNodesByTagName(myNode, "SubGroup")) {
            SubGroup item;
            item = new SubGroup();
            item.deserialize(node);
            _subGroup.add(item);
        }
    }
}

Item.java:

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;

public class Item {
    private Group _group;
    public Group get_group() { return _group; }
    public void set_group(Group group) { _group = group; }

    public void deserialize(String xml) throws ParserConfigurationException, IOException, SAXException, ParseException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
        Document doc = documentBuilder.parse(new ByteArrayInputStream(xml.getBytes("utf-8")));
        Element root = doc.getDocumentElement();
        deserialize(doc.getElementsByTagName("Item").item(0));
    }

    public void deserialize(Node myNode) throws ParseException {
        NodeList nodes;
        _group = new Group();
        _group.deserialize(UtilityMethods.getNodesByTagName(myNode, "Group").get(0));
    }
}

Test.java:

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;

public class Test {
    private ArrayList<Item> _item;
    public ArrayList<Item> get_item() { return _item; }
    public void set_item(ArrayList<Item> item) { _item = item; }

    public void deserialize(String xml) throws ParserConfigurationException, IOException, SAXException, ParseException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
        Document doc = documentBuilder.parse(new ByteArrayInputStream(xml.getBytes("utf-8")));
        Element root = doc.getDocumentElement();
        deserialize(doc.getElementsByTagName("Test").item(0));
    }

    public void deserialize(Node myNode) throws ParseException {
        NodeList nodes;
        _item = new ArrayList<>();
        for (Node node : UtilityMethods.getNodesByTagName(myNode, "Item")) {
            Item item;
            item = new Item();
            item.deserialize(node);
            _item.add(item);
        }
    }
}

Here, the Test class should be able to accept the entire XML string, or a node pointing to the start of the <Test> node, that it can then use to deserialize into itself.

Another note is that this assumes the XML is well-formed.

\$\endgroup\$
  • \$\begingroup\$ "that knows of it's structure and can properly identify and deserialize itself." Why? Sounds very arbitrary. \$\endgroup\$ – abuzittin gillifirca Feb 22 at 14:53
1
\$\begingroup\$

Quick skim simplifications:

  • The way you return in UtilityMethods allows you to not use an explicit else-block. e.g. for getInt8:

    if (input == null || input.equals("")) {
        return 0;
    }
    return Byte.parseByte(input);
    
  • the return statement for getBoolean should make you stumble. Instead of a nested ternary, use a simple or:

    return input.equals("y") || input.equals("yes");
    

    Note that you're currently assuming the value will always be lowers-case. That may not be true :)

  • getNodesByTagName is a bit of a misnomer. You're only searching through the child nodes themselves. As such, this could be more easily expressed in a stream using filter, if only NodeList was Iterable (~sigh). As it stands, the only thing I would advise is to return a List instead of an ArrayList (program against interfaces), as well as a rename to something like childrenWithTagName or something like that.

    If you wanted to search through the whole document tree below the root node, you may want to look into XPath instead.

  • getAttrValue is a bit of a misnomer as well. Nodes in an xml-document have attributes which have values. Those attributes are only defined on Elements. The nodeValue is but one possible attribute of an element, in theory any element can contain any number of attributes.

    As it stands this method is a thin wrapper over getNodeValue that allows you to avoid null pointers.

  • Generally speaking: use List over ArrayList in declarations,


I personally don't very much like the way you named the fields in your class, but I assume that this has some very good reason and at least you're being consistent about it :)
I just wanted to get that off my chest and with that, I'm done.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.