8 Replies Latest reply: Mar 28, 2013 11:08 AM by gimbal2 RSS

    XML Parsing Woes

    999755
      I'm trying to parse XML being returned from a service but having no luck. I've written a unit test to parse a sample that looks like this:


      *<?xml version="1.0" encoding="UTF-8"?>*
      *<ShoppingLists xmlns="http://service.mydomain.com/2011/01" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">*
      *<ShoppingList>*
      *<Id>f4425557-69bf-43d9-8c5b-257e98f4ab40</Id>*
      *<Name>Default</Name>*
      *<DateModified>2013-03-27 16:27:43Z</DateModified>*
      *</ShoppingList>*
      *</ShoppingLists>*


      I've got a CustomerRecord class that contains a List of the ShoppingList elements and expect JAXB to parse exactly one. The unit test runs and seems to parse without error but does not find the record.

      This is the unit test I'm running:
           @Test
           public void should_unmarshall_user_lists() throws JAXBException,
                     JsonGenerationException, JsonMappingException, IOException {
                JAXBContext context = JAXBContext
                          .newInstance(CustomerRecord.class);
                Unmarshaller u = context.createUnmarshaller();
                String listText = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                          + "<ShoppingLists xmlns=\"http://service.mydomain.com/2011/01\" "
                          + "xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">"
                          + "<ShoppingList>"
                          + "<Id>f4425557-69bf-43d9-8c5b-257e98f4ab40</Id>"
                          + "<Name>Default</Name>"
                          + "<DateModified>2013-03-27 16:27:43Z</DateModified>"
                          + "</ShoppingList></ShoppingLists>";
                System.out.println(listText);
                InputStream is = new ByteArrayInputStream(listText.getBytes("UTF-8"));
                CustomerRecord lists = (CustomerRecord) u
                          .unmarshal(is);
                List<ShoppingListInfo> infos = lists.getShoppingLists();
                System.out.println(infos.size() + " LISTS PARSED");
                assertEquals(infos.size(),1);
           }
      These are the data classes:
      package com.sam.dc.wireformats;
      
      import java.util.ArrayList;
      import java.util.List;
      
      import javax.xml.bind.annotation.XmlElement;
      import javax.xml.bind.annotation.XmlElementWrapper;
      import javax.xml.bind.annotation.XmlRootElement;
      
      @XmlRootElement(name="ShoppingLists",namespace="http://service.mydomain.com/2011/01")
      public class CustomerRecord {
           
           List<ShoppingListInfo> shoppingLists;
           
           @XmlElementWrapper(namespace="http://service.mydomain.com/2011/01")
           @XmlElement(name = "ShoppingList",namespace="http://service.mydomain.com/2011/01")
           public List<ShoppingListInfo> getShoppingLists() {
                System.out.println("GET SHOPPING LISTS CALLED");
                if (shoppingLists == null) {
                     shoppingLists = new ArrayList<ShoppingListInfo>();
                }
                return shoppingLists;
           }
           
           public void setShoppingLists(List<ShoppingListInfo> shoppingLists) {
                System.out.println("SET SHOPPING LISTS CALLED");
                this.shoppingLists = shoppingLists;
           }
           
      }
      ...and...
      package com.sam.dc.wireformats;
      
      import java.util.UUID;
      
      import javax.xml.bind.annotation.XmlAccessType;
      import javax.xml.bind.annotation.XmlAccessorType;
      import javax.xml.bind.annotation.XmlElement;
      import javax.xml.bind.annotation.XmlType;
      
      @XmlType(name="ShoppingList", namespace = "http://service.mydomain.com/2011/01")
      @XmlAccessorType(XmlAccessType.FIELD)
      public class ShoppingListInfo {
      
           @XmlElement(name = "Id", namespace = "http://service.mydomain.com/2011/01")
           private UUID id;
           @XmlElement(name = "Name", namespace = "http://service.mydomain.com/2011/01")
           private String name;
           @XmlElement(name = "DateModified", namespace = "http://service.mydomain.com/2011/01")
           private String dateModified;
      
           public String getDateModified() {
                return dateModified;
           }
           
           public void setDateModified(String dateModified) {
                this.dateModified = dateModified;
           }
      
           public UUID getId() {
                return id;
           }
      
           public void setId(UUID id) {
                this.id = id;
           }
      
           public String getName() {
                return name;
           }
      
           public void setName(String name) {
                this.name = name;
           }
      }
      I'm assuming there's something wrong with my annotations, but I've tried all kinds of stuff with no luck. Any help would be most appreciated.

      Thanks!
      Sam

      Edited by: 996752 on Mar 28, 2013 6:24 AM
        • 1. Re: XML Parsing Woes
          gimbal2
          Try reposting everything using \
           tags. This is unreadable.                                                                                                                                                                                                
          • 2. Re: XML Parsing Woes
            999755
            Done.
            • 3. Re: XML Parsing Woes
              gimbal2
              I'm comparing to some JAXB stuff I maintain at work and I see little differences between your setup and mine (besides that in my case the JAXB stuff is generated from an XSD using a Maven task). I note a few things that jump at me:

              - I don't have any @XmlElementWrapper elements
              - the @XmlElement annotations don't have any namespace attribute (but then again: neither does the XML input data)
              - all the annotations are on the fields, not the getters

              Also I wouldn't put the XML data hardcoded as that is very error-prone, I would put it in a resource file on the test classpath and simply load that using getClass().getResourceAsStream(). Of course if the XML data were broken, you'd get a parsing error for sure.

              Just for the sake of completeness, try surrounding your test code with a try/catch block to see if any exception is not sneakily happening and disappearing in the depths of JUnit.
              • 4. Re: XML Parsing Woes
                999755
                Try/catch catches no exceptions.

                I tried it without the namespace attribute and got an error
                EXCEPTION CAUGHT unexpected element (uri:"http://service.mywebgrocer.com/2011/01", local:"ShoppingLists"). Expected elements are <{}ShoppingLists>
                • 5. Re: XML Parsing Woes
                  999755
                  Got it!

                  Removing @XmlElementWrapper made it work. It never occurred to me it could work without this--I thought the wrapper was absolutely required for a collection of elements.

                  Thanks.
                  • 6. Re: XML Parsing Woes
                    gimbal2
                    996752 wrote:
                    Got it!

                    Removing @XmlElementWrapper made it work. It never occurred to me it could work without this--I thought the wrapper was absolutely required for a collection of elements.

                    Thanks.
                    Yeah baby. But now I wonder what the heck that annotation does.
                    • 7. Re: XML Parsing Woes
                      jtahlborn
                      gimbal2 wrote:
                      996752 wrote:
                      Got it!

                      Removing @XmlElementWrapper made it work. It never occurred to me it could work without this--I thought the wrapper was absolutely required for a collection of elements.

                      Thanks.
                      Yeah baby. But now I wonder what the heck that annotation does.
                      it indicates that there is a wrapper element around a collection of elements. in this case, however, the "ShoppingLists" element was actually the root element, so there was no wrapper. you would need the annotation if, instead, your xml looked something like this and you didn't want a separate "ShoppingLists" model class:
                      <CustomerRecord>
                        <ShoppingLists>
                          <ShoppingList>...</ShoppingList>
                          <ShoppingList>...</ShoppingList>
                        <ShoppingLists>
                      </CustomerRecord>
                      • 8. Re: XML Parsing Woes
                        gimbal2
                        Thanks, now I don't need to research it myself :)