Announcement Announcement Module
Collapse
No announcement yet.
Reading mutilple element types with StaxEventItemReader Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Reading mutilple element types with StaxEventItemReader

    Dear Friends,

    I need to read multiple types of elements from XML input and bind to objects. I believe that is possible using StaxEventItemReader, but i don't know how.

    If it's not possible, please, tell me how can I do that using other ItemReader? Or if necessary, suggestions of custom XMLItemReader implementations.

    Thanks from Brazil.

    Fabiano

  • #2
    You need to configure your StaxEventItemReader and provide it with an Spring OXM Unmarshaller. The latter will convert XML fragments into domain objects. This is explained here. Note you can use any marshalling library supported by Spring OXM (XStream, JAXB2, etc.).

    Comment


    • #3
      Thank you for reply!

      I undestand you, but the configuration allows only one fragmentRootElementName. Because of that, i can't map other element type at the same reader.

      I have some Spring Batch samples using StaxEventItemReader, but in everyone, the author didn't use multiple input type.

      Comment


      • #4
        MultiFragmentStaxEventItemReader

        I had the same problem and came up with the following solution:

        Code:
        package x.y.z;
        
        import java.util.List;
        
        import javax.xml.namespace.QName;
        import javax.xml.stream.XMLEventReader;
        import javax.xml.stream.XMLStreamException;
        import javax.xml.stream.events.StartElement;
        
        import org.apache.commons.lang.Validate;
        import org.springframework.batch.item.xml.StaxEventItemReader;
        import org.springframework.beans.factory.InitializingBean;
        import org.springframework.dao.DataAccessResourceFailureException;
        
        /**
         * An enhanced <code>StaxEventItemReader</code> to handle multiple node element
         * names that need to be extracted.
         * 
         * @author mazehnder
         */
        public class MultiFragmentStaxEventItemReader<T> extends StaxEventItemReader<T>
            implements InitializingBean {
        
          private List<String> fragmentRootElementNames;
          
          public void setFragmentRootElementNames(List<String> fragmentRootElementNames) {
            if (fragmentRootElementNames == null || fragmentRootElementNames.isEmpty()) {
              throw new IllegalArgumentException("At least one root element name must be provided");
            }
            // to satisfy super class's afterPropertiesSet
            super.setFragmentRootElementName(fragmentRootElementNames.get(0));
            this.fragmentRootElementNames = fragmentRootElementNames;
          }
          
          @Override
          protected boolean moveCursorToNextFragment(XMLEventReader reader) {
            try {
              while (true) {
                while (reader.peek() != null && !reader.peek().isStartElement()) {
                  reader.nextEvent();
                }
                if (reader.peek() == null) {
                  return false;
                }
                QName startElementName = ((StartElement) reader.peek()).getName();
                String elementName = startElementName.getLocalPart();
                if (fragmentRootElementNames.contains(elementName)) {
                  return true;
                } else {
                  reader.nextEvent();
                }
              }
            } catch (XMLStreamException e) {
              throw new DataAccessResourceFailureException("Error while reading from event reader", e);
            }
          }
          
          @Override
          public void afterPropertiesSet() throws Exception {
            super.afterPropertiesSet();
            Validate.notNull(fragmentRootElementNames, "Property fragmentRootElementNames must not be null");
            Validate.notEmpty(fragmentRootElementNames, "Property fragmentRootElementNames must not be empty");
          }
        }

        Comment


        • egaurav
          egaurav commented
          Editing a comment
          It doesn't work in my case and not able to read the neste inner elements.I am not able to read address List elements and their phone elements :-
          I have used jaxb to unmarshall and take rootfragment=merchant,consumer,address,phone.

          Here is my xml:-

          http://forum.spring.io/forum/spring-...nt-item-reader
          Last edited by egaurav; Mar 25th, 2014, 11:06 PM.

      • #5
        Okay! Thank you very much!

        I'm going to implement some tests!!!

        Comment


        • #6
          Pay attention that the implementation of MultiFragmentStaxEventItemReader does not support restart. jumpToItem is not overridden and the implementation provided by StaxEventItemReader is based on the first fragmentRootElementName.

          Comment


          • #7
            MultiFragmentStaxEventItemReader with restart

            hi there,

            Is it possible to make MultiFragmentStaxEventItemReader restartable? Make it restartable is a major issue in our project at the moment where we have to read complex xml files.

            Hope anyone has a clue.

            Thanks in advance!
            Wop

            Comment


            • #8
              Here is my implementation of jumpToItem(int itemIndex)
              Code:
              	@Override
              	protected void jumpToItem(int itemIndex) throws Exception {
              		for (int i = 0; i < itemIndex; i++) {
              			readToStartFragment();
              			readToEndFragment();
              		}
              	}
              
              	private void readToStartFragment() throws XMLStreamException {
              		while (true) {
              			XMLEvent nextEvent = eventReader.nextEvent();
              			if (nextEvent.isStartElement() 
              					&& fragmentRootElementNames.contains(((StartElement) nextEvent).getName().getLocalPart())) {
              				return;
              			}
              		}
              	}
              
              	private void readToEndFragment() throws XMLStreamException {
              		while (true) {
              			XMLEvent nextEvent = eventReader.nextEvent();
              			if (nextEvent.isEndElement()
              					&& fragmentRootElementNames.contains(((EndElement) nextEvent).getName().getLocalPart())) {
              				return;
              			}
              		}
              	}

              Comment


              • #9
                thank you very much. I think I can use that.
                the only thing that is not very clear to me at the moment is how, with reading complex xmlfiles, itemIndex (READ_COUNT) is set.
                Does the framework do that?
                Has it to be done programmatically?
                Is it configuration?

                Comment


                • #10
                  Your class must extend AbstractItemCountingItemStreamItemReader.

                  See the javadoc of AbstractItemCountingItemStreamItemReader for more info.

                  Comment


                  • #11
                    thanks again!
                    i'll try to implement it.

                    Comment


                    • #12
                      This is enlightening. Is there anyway the StaxEvenItemReader (or its variants) can process nest items. I have to parse a large file with complex structure like below. I am wondering if Spring Batch is good fit for it. Stuck here. Any idea would be much appreciated !

                      <xml...>
                      <records>
                      <record id="1">
                      <simpleValue>1</simpleValue>
                      <recordDetail>
                      <name>detail1</name>
                      <count>3</count>
                      </recordDetail>
                      <recordlinks>
                      <recordlink>
                      ....
                      </recordlink>
                      <recordlink>
                      ....
                      </recordlink>
                      </record>
                      <record id="2">
                      <simpleValue>2</simpleValue>
                      <recordDetail>
                      <name>detail2</name>
                      <count>3</count>
                      </recordDetail>
                      <recordlinks>
                      <recordlink>
                      ....
                      </recordlink>
                      <recordlink>
                      ....
                      </recordlink>
                      </record>
                      <records>
                      </xml>

                      Simon

                      Comment


                      • #13
                        Check out the details on the xstream marshaller (org.springframework.oxm.xstream.XStreamMarshaller ). If you don't like how it handles the XML then swap it out for a different marshaller.

                        Jeff

                        Comment


                        • #14
                          Actually that's exactly what in my mind. Having spent too much time to make XstreamMarshaller work. Still I couldn't get the attribute correctly. Will give Jaxb a try as I red nice thing about them. Thanks.

                          Originally posted by visualjeff View Post
                          Check out the details on the xstream marshaller (org.springframework.oxm.xstream.XStreamMarshaller ). If you don't like how it handles the XML then swap it out for a different marshaller.

                          Jeff

                          Comment


                          • #15
                            Just a follow-up. I tried playing with Jaxb2 Marshaller. It works like a charm. I am new to both xstream and jaxb2. Guess I am going to say bye-bye to xstream.


                            Originally posted by fwr2010 View Post
                            Actually that's exactly what in my mind. Having spent too much time to make XstreamMarshaller work. Still I couldn't get the attribute correctly. Will give Jaxb a try as I red nice thing about them. Thanks.

                            Comment

                            Working...
                            X