简体   繁体   中英

Parsing an XML in Java using STax

This, even to me, seems like a silly question but then is one of those to which i cant find an answer.

Im trying to parse an XML using STax in Java and the XMl im trying to parse looks like this --

<?xml version="1.0" encoding="UTF-8"?>
<Macros>
    <MacroDefinition>
            <MacroName>
                <string>Macro1</string>
            </MacroName>
    </MacroDefinition>
</Macros>

Now i have a Macro class as follows --

 public class Macro {
    private String name; 

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    }

I also have a parser class from where i try to convert the XML into an object of the 'Macro' class. The parser class snippet is as follows --

public class StaxParser {
    static final String MACRODEFINITION = "MacroDefinition";
    static final String MACRONAME = "MacroName";
    static final String STRING = "string";

    @SuppressWarnings({ "unchecked", "null" })
    public List<Item> readMacro(String configFile) {
        List<Macro> macroList = new ArrayList<Macro>();
        try {
            // First create a new XMLInputFactory
            XMLInputFactory inputFactory = XMLInputFactory.newInstance();
            // Setup a new eventReader
            InputStream in = new FileInputStream(configFile);
            XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
            // Read the XML document
            Macro macro = null;

            while (eventReader.hasNext()) {
                XMLEvent event = eventReader.nextEvent();

                if (event.isStartElement()) {
                    StartElement startElement = event.asStartElement();
                    if (startElement.getName().getLocalPart() == (MACRODEFINITION)) {
                        macro = new Macro();

                    }

                    if (event.isStartElement()) {
                        if (event.asStartElement().getName().getLocalPart()
                                .equals(MACRONAME)) {
                            Iterator<Attribute> attributes = event
                                    .asStartElement().getAttributes();
                            while (attributes.hasNext()) {
                                Attribute attribute = attributes.next();
                                if (attribute.getName().toString()
                                        .equals(STRING)) {
                                    macro.setMacroName(event.asCharacters()
                                            .getData());
                                }
                            }
                            event = eventReader.nextEvent();
                            continue;
                        }
                    }
                }
                // If we reach the end of an item element we add it to the list
                if (event.isEndElement()) {
                    EndElement endElement = event.asEndElement();
                    if (endElement.getName().getLocalPart() == (MACRODEFINITION)) {
                        macroList.add(macro);
                    }
                }

            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
        return macroList;
    }

}

The problem im facing is that the parser is not able to read the child nodes of 'MacroName'. Im thinking getAttributes is what is causing it not to work but have no clue of what method i should be calling to get the child nodes of any particular node.
Any help with this would be greatly appreciated.
Thanks
p1nG

Sorry to say that, but your code has many issues and doesn't even compile.

First of all, the return type should be List<Macro> , since the Macro class doesn't inherit from, nor implement, the Item .

Second, you should ensure a safe nesting, to follow the schema of your XML, not arbitrarily test for event name equality and create Macro objects here and there along the way. If you plan to retreive also other data besides the macro name, you can't get away with just checking for the STRING event occurence.

Third, it's useless to nest the same checks, eg event.isStartElement() .

Fourth, you should provide a Source or a Reader or a Stream to a class such as the StaxParser, not directly a filename, but I didn't include this change to avoid breaking your API.

class StaxParser {
    static final String MACRODEFINITION = "MacroDefinition";
    static final String MACRONAME = "MacroName";
    static final String STRING = "string";

    @SuppressWarnings({ "unchecked", "null" })
    public List<Macro> readMacro(final String configFile) {
        final List<Macro> macroList = new ArrayList<Macro>();
        try {
            // First create a new XMLInputFactory
            final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
            // Setup a new eventReader
            final InputStream in = new FileInputStream(configFile);
            final XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
            // Read the XML document

            final Template template = getTemplate(eventReader);
            macroList.addAll(template.process(null, getMacrosProcessor(template)));

        } catch (final FileNotFoundException e) {
            e.printStackTrace();
        } catch (final XMLStreamException e) {
            e.printStackTrace();
        }
        return macroList;
    }

    interface Template {
        <T> T process(String parent, EventProcessor<T> ep) throws XMLStreamException;
    }

    static Template getTemplate(final XMLEventReader eventReader) {
        return new Template() {
            @Override
            public <T> T process(final String parent, final EventProcessor<T> ep) throws XMLStreamException {
                T t = null;
                boolean process = true;
                while (process && eventReader.hasNext()) {
                    final XMLEvent event = eventReader.nextEvent();
                    if (ep.acceptsEvent(event)) {
                        t = ep.processEvent(event);
                    }
                    if (event.isEndElement()) {
                        if (null != parent && parent.equals(event.asEndElement().getName().getLocalPart())) {
                            process = false;
                        }
                    }
                }
                return t;
            }
        };
    }

    interface EventProcessor<T> {
        boolean acceptsEvent(XMLEvent event);

        T processEvent(XMLEvent event) throws XMLStreamException;
    }

    static EventProcessor<List<Macro>> getMacrosProcessor(final Template template) {
        final List<Macro> macroList = new ArrayList<Macro>();
        return new EventProcessor<List<Macro>>() {
            @Override
            public boolean acceptsEvent(final XMLEvent event) {
                return event.isStartElement()
                        && MACRODEFINITION.equals(event.asStartElement().getName().getLocalPart());
            }

            @Override
            public List<Macro> processEvent(final XMLEvent event) throws XMLStreamException {
                macroList.add(template.process(MACRODEFINITION, getMacroDefinitionProcessor(template)));
                return macroList;
            }
        };
    }

    static EventProcessor<Macro> getMacroDefinitionProcessor(final Template template) {
        return new EventProcessor<Macro>() {
            @Override
            public boolean acceptsEvent(final XMLEvent event) {
                return event.isStartElement() && MACRONAME.equals(event.asStartElement().getName().getLocalPart());
            }

            @Override
            public Macro processEvent(final XMLEvent event) throws XMLStreamException {
                final Macro macro = new Macro();
                macro.setName(template.process(MACRONAME, getMacroNameProcessor(template)));
                return macro;
            }
        };
    }

    static EventProcessor<String> getMacroNameProcessor(final Template template) {
        return new EventProcessor<String>() {
            @Override
            public boolean acceptsEvent(final XMLEvent event) {
                return event.isStartElement() && STRING.equals(event.asStartElement().getName().getLocalPart());
            }

            @Override
            public String processEvent(final XMLEvent event) throws XMLStreamException {
                return template.process(STRING, getStringProcessor());
            }
        };
    }

    static EventProcessor<String> getStringProcessor() {
        return new EventProcessor<String>() {
            @Override
            public boolean acceptsEvent(final XMLEvent event) {
                return event.isCharacters();
            }

            @Override
            public String processEvent(final XMLEvent event) throws XMLStreamException {
                return event.asCharacters().getData();
            }
        };
    }
}

First notice that Macro1 is not XML attribute, so event attributes will be empty. Code after changes ( I have only shown lines of code that may be of interest ):

  if (event.isStartElement()
     && event.asStartElement().getName().getLocalPart().equals(STRING)) {
          if (macro == null) {
               macro = new Macro();
          }
           macro.setName(eventReader.getElementText());
  }

A few tips: never ever compare strings using == use equals method. If you need full working example I could post my solution, but it is bit more complicated.

You have to change macro.setMacroName(event.asCharacters().getData());

to macro.setMacroName(attribute.getvalue().toString());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM