3

I'm trying to parse an xml file using JAXB. My problem is that I need to skip the root node, If I delete it from the xml file I get what I need, otherwise - I get an empty object.

I'll give a simplified xml and my code (It behaves the same way):

XML:

<?xml version="1.0" encoding="utf-8"?>
<Root>
<!--  <Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="Office.xsd">
-->
    <Office>
        <Employees>
            <Employee>
                <name>George</name>
                <rank>3</rank>
            </Employee>
            <Employee>
                <name>Michael</name>
                <rank>5</rank>
            </Employee>
            <Employee>
                <name>Jeff</name>
                <rank>1</rank>
            </Employee>
            <Employee>
                <name>Todd</name>
                <rank>7</rank>
            </Employee>
            <Employee>
                <name>Jessica</name>
                <rank>5</rank>
            </Employee>
        </Employees>
    </Office>
</Root>

Office class:

import javax.xml.bind.annotation.*;
import java.util.Vector;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Office {

   @XmlElementWrapper(name = "Employees")
   @XmlElement(name = "Employee")
   private Vector<Employee> employees;

}

Employee class:

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {

   @XmlElement(name="name")
   private String name; 
   @XmlElement(name="rank")
   private int rank;


   public void promote() {
      rank++;
   }

}

Driver:

import javax.xml.stream.*; 
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.Marshaller;

import java.io.FileReader;
public class Driver {

     public static void main (String[] args) {
        parseOffice();

     }

     public static void parseOffice() {
        try {

          XMLInputFactory f = XMLInputFactory.newInstance();
          XMLStreamReader reader = f.createXMLStreamReader(new FileReader("Office.xml"));

            JAXBContext context = JAXBContext.newInstance(Office.class);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            Office office = unmarshaller.unmarshal(reader, Office.class).getValue();

            Marshaller marshaller = context.createMarshaller();
          marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
          marshaller.marshal(office, System.out);

         } 
         catch (Exception e) {
             e.printStackTrace();
         }
      }
}
1
  • I omitted output because the post is long enough and it can be easily replicated; however If there's a need for me to include it, I'll do so.
    – Michael
    Commented Dec 13, 2014 at 18:54

3 Answers 3

4

You can parse the XML with a StAX XMLStreamReader, then advance it to the element you want to unmarshal, and then unmarshal it.

I posted a full example that should help on the related question linked below:

0
4

Why don't you create a generic root element?

@XmlRootElement(name="Root" ...)
public class Root {
   @XmlAnyElement(lax=true)
   private Object content;
}

Add it to your context and unmarshal. You should get a JAXBElement<Office> as content.

3
  • 2
    Well, obviously I can do that - but that's not what I want Reason: I want to know how to do it without adding anything, if possible. I need a way to ignore or skip the root element. I guess, I should have mentioned it in my question. +1
    – Michael
    Commented Dec 13, 2014 at 19:15
  • 3
    @Michael You'll have to add something in any case. Either an additional root element mapping or the skipping code on the lower level. I'd say an extra root class is better as it's the same abstraction level as other mappings and woould work with anything (SAX, StAX, DOM). However you may have technical reasons (not transparent to me at the moment) to want to have it otherwise. Then do it on the lower level - skip StAX states, filter SAX events, pre-dive in the DOM.
    – lexicore
    Commented Dec 13, 2014 at 19:40
  • Out of curiousity, how would I convert the content object to an office object? I could do it as Panther suggested, but is there an easy way to convert in a more generic fashion?
    – Joseph
    Commented Jan 21, 2016 at 18:55
1

Simply add the root class in hierarcy. And get Office class from Root class.

Root Class:-

import javax.xml.bind.annotation.*;
import java.util.Vector;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {


 @XmlElement(name = "Office")
 private Office office; 
 }

Office class

 import javax.xml.bind.annotation.*;
 import java.util.Vector;


  @XmlAccessorType(XmlAccessType.FIELD)
  public class Office {

  @XmlElementWrapper(name = "Employees")
  @XmlElement(name = "Employee")
  private Vector<Employee> employees;

  }

Change in parse method :-

       JAXBContext context = JAXBContext.newInstance(Root.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        Root root = unmarshaller.unmarshal(reader, Root.class).getValue();
        Office office = root.getOffice();
3
  • You limit the content to Office in the Root which isn't flexible.
    – lexicore
    Commented Dec 13, 2014 at 19:36
  • @lexicore Thanks for input , i was just trying to solve his problem. As he doesn't seems to want to do much changes
    – Panther
    Commented Dec 13, 2014 at 19:38
  • No problem, was just a hint. Adn you're right, both of our anwers seem to be not suitable for the OP. This happens.
    – lexicore
    Commented Dec 13, 2014 at 19:42

Not the answer you're looking for? Browse other questions tagged or ask your own question.