Read XML file with only one parent node

1

I need to import an XML file that has the following structure:

<?xml version="1.0" encoding="UTF-8"?>
<Registos>3</Registos>

<Socios>

    <id>1</id>
    <nome>Paulo</nome>
    <email>[email protected]</email>

    <id>2</id>
    <nome>Vitor</nome>
    <email>[email protected]</email>

    <id>3</id>
    <nome>Rute</nome>
    <email>[email protected]</email>

</Socios>

I'm using the DOM to read the file but my problem is that it only has a Parent Node

<Socios>...</Socios>

Can someone help me?

    
asked by anonymous 26.12.2015 / 13:43

2 answers

1

This XML has a basic problem, which is to have more than one root tag ( <Registos /> and <Socios /> ), making it an invalid XML. Since it is not a valid format you will not be able to read it with DOM because it expects a valid XML, so an error similar to this should occur:

[Fatal Error] :4:2: The markup in the document following the root element must be well-formed.
    lineNumber: 4; columnNumber: 2; The markup in the document following the root element must be well-formed.

Correct is you already expect well-formed XML, as suggested at the end of this answer. If this is beyond what you can do, if it is delivered by some other application that you do not own, as in a third-party integration, then you will have to bypass this set the received XML. The suggestion is to form valid XML from what it receives, including any root tag. I'll use <Data /> as an example.

Our code for transforming XML into a valid format will look something like this:

private static final String DATA_ROOT_START = "<Data>";
private static final byte[] DATA_ROOT_START_BYTES = DATA_ROOT_START.getBytes();
private static final String DATA_ROOT_END = "</Data>";
private static final byte[] DATA_ROOT_END_BYTES = DATA_ROOT_END.getBytes();
private static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
private static final byte[] XML_HEADER_BYTES = XML_HEADER.getBytes();
private static final int XML_HEADER_LENGTH = XML_HEADER.length();

public InputStream fixXML(final InputStream is) throws Exception {
    try (final InputStream startIS = new ByteArrayInputStream(DATA_ROOT_START_BYTES);
            final InputStream headerIS = new ByteArrayInputStream(XML_HEADER_BYTES);
            final InputStream endIS = new ByteArrayInputStream(DATA_ROOT_END_BYTES)) {
        is.skip(XML_HEADER_LENGTH);
        final List<InputStream> streams = Arrays.asList(headerIS, startIS, is, endIS);
        return new SequenceInputStream(Collections.enumeration(streams));
    }
}

This guy retrieves InputStream of the contents of your XML, without the header. including the root tag we chose and also the header (this is not mandatory in some cases).

Once corrected we can read the XML, something like this:

final AssetManager manager = getAssets();
final InputStream is = manager.open("socios.xml");
final InputStream fixedXMLIS = fixXML(is);

final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder db = factory.newDocumentBuilder();
final InputSource inputSource = new InputSource(fixedXMLIS);
final Document document = db.parse(inputSource);

final NodeList socios = document.getElementsByTagName("Socios");
final int sociosSize = socios.getLength();
for (int i = 0; i < sociosSize; i++) {
    final Node socio = socios.item(i);
    final NodeList socioChilds = socio.getChildNodes();
    final int socioChildsSize = socioChilds.getLength();
    for (int j = 0; j < socioChildsSize; j++) {
        final Node e = socioChilds.item(j);
        Log.d("XMLFIXSample", e.getTextContent().trim());
    }
}

This will generate something like this:

I/XMLFIXSample(N): 1
I/XMLFIXSample(N): Paulo
I/XMLFIXSample(N): [email protected]
I/XMLFIXSample(N): 2
I/XMLFIXSample(N): Vitor
I/XMLFIXSample(N): [email protected]
I/XMLFIXSample(N): 3
I/XMLFIXSample(N): Rute
I/XMLFIXSample(N): [email protected]

After this you use the values according to your need, filling directly some field on the screen, entity, etc.

Note: This code is an example that transforms XML into a valid format, adapts it and improves it according to your needs, realizes that it uses Java7 things, so if you want to use it correctly, set the target to compile for a version that Android supports;)

Obs2: I do not know if you are generating this XML, but if it is, consider putting it in a valid format with just a root node, something like this:

<?xml version="1.0" encoding="UTF-8"?>
<Data>
    <Registos>3</Registos>

    <Socios>
        <id>1</id>
        <nome>Paulo</nome>
        <email>[email protected]</email>

        <id>2</id>
        <nome>Vitor</nome>
        <email>[email protected]</email>

        <id>3</id>
        <nome>Rute</nome>
        <email>[email protected]</email>
    </Socios>
</Data>

Another thing you can do is have a root tag for your partner data, something like this:

<Socio>
    <id>1</id>
    <nome>Paulo</nome>
    <email>[email protected]</email>
</Socio>

Generating this end result:

<?xml version="1.0" encoding="UTF-8"?>
<Data>
    <Registos>3</Registos>

    <Socios>
        <Socio>
            <id>1</id>
            <nome>Paulo</nome>
            <email>[email protected]</email>
        </Socio>

        <Socio>
            <id>2</id>
            <nome>Vitor</nome>
            <email>[email protected]</email>
        </Socio>

        <Socio>
            <id>3</id>
            <nome>Rute</nome>
            <email>[email protected]</email>
        </Socio>
    </Socios>
</Data>

This would make it even simpler to work with XML

    
26.12.2015 / 17:27
0

Good Vitor I am not an XML expert, but in his file the root is missing a tag to group all the others. example Here . And, of course, all nodes will only have 1 parent, however the contract is possible (one parent node has multiple children)

Tip: Group partner tags and records within a root

And here you can find something to help you read the value of nodes

    
26.12.2015 / 16:19