Read XML file with only one Parent Node

Asked

Viewed 185 times

2

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 DOM to read the file but my problem is that it only has a Nodo Pai

<Socios>...</Socios>

Can someone help me?

2 answers

2


This XML of yours 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, then 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.

The correct thing is that you already expect a 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 it by adjusting the received XML. The suggestion is to form a valid XML from what you receive, including any root tag. I will use <Data /> as an example.

Our code to transform 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 recovers the InputStream the contents of your XML, without the header. including the root tag we have chosen and also the header (this is not required in certain cases).

Once fixed we can read the XML, something like that:

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 as you need, filling directly some field on the screen, entity, etc.

Obs.: this code is an example that transforms XML into a valid format, adapts it and improves it as your needs, realize that it uses things from Java7, so if you want to use it like this, configure the target to compile for a version that Android supports ;)

Obs2.: I don’t 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 the partner data, something like that:

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

Generating this final 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

  • Bruno César, thank you for your reply. My "socios.xml" file is in Sdcard0, but I’m not getting the "final Inputstream is = manager.open(arquivoXML );" line to find the XML file. I have the reading permission "READ_EXTERNAL_STORAGE" and the following configuration: File Sdcardroot = Environment.getExternalStorageDirectory(); File arquivoXML = new File(Sdcardroot, "socios.xml");?

  • @Vitormendanha there is already something else, maybe you better ask another question, if you need. Before researching here on Sopt on the subject, there are some questions, as this

  • 1

    I was able to solve the problem: Replace the line "final Inputstream is = manager.open("socios.xml");" with "Inputstream is = new Bufferedinputstream(new Fileinputstream(arquivoXML ));".

  • @Vitormendanha this is it :)

1

Good Victor I am not an expert in XML , but in his file lacks a root tag to group all the others. example Here. And, by logic, all nodes will have only 1 Parent, however the contract is possible ( a parent having several children)

Tip: Group membership and record tags within a root

And at this link can be found something to help you read the value of nodes

  • Bill Silva, thanks for the answer. From what I understand, it will not be easy to solve my problem, because I have to read several different files...

Browser other questions tagged

You are not signed in. Login or sign up in order to post.