javadeserializationemfeclipse-emfecore

Loading serialized Ecore Model as XMI fails to load containments correctly


I am creating an instance of an Ecore model dynamically in memory, serialize it as XMI and then the user might modify it during the runtime of my app. So I then deserialize the XMI and compare it with the prior model version I still have in memory. When doing this, the containment-references from within my model seem to get lost. Let's say the underlying Ecore-Model looks like this: It has a class State and a class Transition, whereas Transition is contained within State through the containment-reference named transition with cardinality 0..*. So a simple serialized XMI of a model instance might look like this:

<?xml version="1.0" encoding="ASCII"?>
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:designmodel="http://www.example.org/designmodel">
  <designmodel:State xmi:id="0f359d4a-5154-462c-90aa-e125197cdb6d" name="Ready">
    <transition href="#7880aa8f-1e86-42e0-a212-e91326292d31"/>
  </designmodel:State>
  <designmodel:Transition xmi:id="7880aa8f-1e86-42e0-a212-e91326292d31" name="switchToWaiting"/>
</xmi:XMI>

Now, when checking the model version I have in memory, everything is as expected and calling eContainer() on the Transition-object returns the respective State-object it is contained in. But when doing the same for the deserialized XMI model-instance, eContainer() returns NULL.

When stepping through the debugger and looking at the respective XMIResource objects I can see the same: the eContainer-property is NULL for the Transition-object for the deserialized XMIResource but set correctly for the one I keep in memory. So it looks like the containments get lost during serialization/deserialization. Serialization for me looks like this:

ResourceSet savingResSet = new ResourceSetImpl();
savingResSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xmi", new XMIResourceFactoryImpl());

XMIResource savingRes = (XMIResource) savingResSet.createResource(URI.createFileURI(outputPath), null);
savingRes.getDefaultSaveOptions().put(XMIResource.OPTION_KEEP_DEFAULT_CONTENT, Boolean.TRUE);


//adding the generated elements to the resource
generatedDesignmodelElements().forEach(e -> {
    if(e != null) {
        savingRes.getContents().add(e);
        savingRes.setID(e, UUID.randomUUID().toString());
    }
});

//saving it
try {
    savingRes.save(Collections.EMPTY_MAP);
} catch (IOException e) {
    e.printStackTrace();

Deserialization simply like this:

ResourceSet resSet = new ResourceSetImpl();      
resSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xmi", new XMIResourceFactoryImpl());
    XMIResource updatedModel =(XMIResource)resSet.getResource(URI.createFileURI(sourcePath), true);

//iterating over the objects directly contained in the resource (so all State- and Transition-objects get reached here, since they are root objects themselves)
updatedModel.getContents().forEach(updatedModelElement -> {
    System.out.println(updatedModelelement + updatedModelElement.eContainer()); //this is always NULL
});

So: Why does this eContainer()-call always returns NULL for the XMIResource that I deserialized, but behaves properly when being called on the XMIResource I have in memory?

Thanks a lot :)


Solution

  • I solved it on my own: The mistake was, that I added contained EObjects on their own as well (so additionally to them being implicitly added to the resource through their container-object). Simply checking for whether a created EObject has a container before adding it to the resource I then serialize at the end solved everything.