简体   繁体   中英

AtomicInteger increments when using JAXB unmarshaller on complex class

I have a problem regarding AtomicInteger when using JAXB unmarshaller. I have the following example code that I am trying to unmarshal the MyTree from an xml file. I create a unique id for each Vertex using AtomicInteger. When unmarshalling the MyTree, it gets incremented when creating Edges. If I have three vertex in the vertexList and two edges in edgeList in myTree.xml, after unmarshalling the nextID for creating a new vertex will create 8 instead of 4. Since for each edge, it adds a vertex for sourceVertex and targetVertex. Can you please help me to figure out what I am doing wrong? and how can I overcome this issue. Thanks alot. (I am really new with JAVA, and JAXB)

JAXBContext context= JAXBContext.newInstance(MyTree.class);
Unmarshaller unmarshaller= context.createUnmarshaller();
MyTree newTree= (MyTree) unmarshaller.unmarshal(new File("MyTree.xml"));


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

   ArrayList<Vertex> vertexList =new ArrayList<Vertex>();
   ArrayList<Edge> edgeList = new ArrayList<Edge>();

   public MyTree() {

   }
   ...
}

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

  public int vertexId;
  private static AtomicInteger nextId = new AtomicInteger(0);
  public Vertex() {
    this.vertexId=nextId.incrementAndGet();     
  }
  ...
}

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

    private Vertex sourceVertex;
    private Vertex targetVertex;
    private EdgeType edgeType;

    public Edge () {

    }
    ... 
 }
   enum EdgeType 
   {
     White,
     Red, 
     Blue;
   }

It's hard to tell from the code you gave what the actual best solution would be, but you could do something like this:

From what I can tell, there are two ways to create a Vertex , by constructor or by deserialization, the latter one creating the problem by calling the constructor too often. So remove the id management to an additional class and only request an Id when you're sure you need it.

First, you'll need to refactor your code to the constructor not incrementing the counter. So

@XmlRootElement
class Vertex {
    private int vertexId;
    public Vertex() { // initialize without incrementing the counter
    }
}

Move the id management to a separate class. Eg

class VertexManager {
    // Singleton
    private static VertexManager INSTANCE;
    private VertexManager() { }
    public static VertexManager getInstance() {
        if (INSTANCE == null) { INSTANCE = new VertexManager(); }
        return INSTANCE;
    }

    // keep track of the ids
    private AtomicInteger currentId = new AtomicInteger();

    // create new vertex
    public static Vertex create() {
        Vertex created = new Vertex();
        register(created);
        return created;
    }
    // add previously created vertex
    public void register(Vertex v) {
        int id = currentId.incrementAndGet();
        v.setId(id);
    }
}

Now all of your current code relies on the constructor incrementing it, you have to make sure all of these places use VertexManager#create() instead! I suggest setting the Vertex constructor to private to provoke compiler errors where it's used, then reset after you changed them all.

The register method you can use for when your deserialization is finished; after the tree is read, all Vertex es are stable - but they still need to assign their Ids. So

JAXBContext context= JAXBContext.newInstance(MyTree.class);
Unmarshaller unmarshaller= context.createUnmarshaller();
MyTree newTree= (MyTree) unmarshaller.unmarshal(new File("MyTree.xml"));
newTree.getVertices().forEach(v -> VertexManager.getInstance().register(v));

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