If you are somewhat familiar with the Eclipse Modeling Framework (EMF) you might know, that you can generate Ecore meta-models from UML, XSD and annotated Java interfaces. This feature is well-documented and it is a great way to lower the barrier of entry for new users. However, the textbook approach requires you to generate an Ecore meta-model, and the corresponding Java implementation.
In this article I will show you how to load an arbitrary XML file — provided you have the schema for it — into EMF, without generating any additional artifacts. Why would anyone want to do that, and deal with instances of DynamicEObjectImpls as opposed to meaningfully named domain model classes? In my case I only needed a way to feed the loaded objects directly into EMF Compare, in order to compare two XML files by content, as opposed to textual representation.
First the source code, then some explanation.
// generate EPackages from schemas
XSDEcoreBuilder xsdEcoreBuilder = new XSDEcoreBuilder();
Collection generatedPackages = xsdEcoreBuilder.generate(schemaURI);
// register the packages loaded from XSD
for (EObject generatedEObject : generatedPackages) {
if (generatedEObject instanceof EPackage) {
EPackage generatedPackage = (EPackage) generatedEObject;
EPackage.Registry.INSTANCE.put(generatedPackage.getNsURI(),
generatedPackage);
}
}
// add file extension to registry
ResourceFactoryRegistryImpl.INSTANCE.getExtensionToFactoryMap()
.put(MY_FILE_EXTENSION, new GenericXMLResourceFactoryImpl());
The first step is loading the schemas and generating EPackages from them. XSDEcoreBuilder provides a convenient method which does both. The return value is of the type Collection<EObject> as opposed to Collection<EPackage>, so the next step is iterating over that collection, casting the items and registering them in the global package registry. This ensures that the generated packages will be available in the whole application. Finally, if your XML files have a custom extension, add that as a key to the resource factory registry, with an GenericXMLResourceFactoryImpl instance as the value. By doing that you make sure, that EMF will know which resource factory to use when loading or saving files of this type.
After that you can load you XML files like you would normally do:
ResourceSet resourceSet = ResourceSetFactory.createResourceSet(); Resource resource = resourceSet.getResource(xmlURI, true); resource.load(Collections.EMPTY_MAP); EObject root = resource.getContents().get(0); ...
If you want to compare the contents of two XML files you just loaded, you can use EMF Compare’s fairly easy and clean API:
MatchModel match = MatchService.doMatch(model1, model2, Collections.emptyMap()); DiffModel diff = DiffService.doDiff(match, false);
For more details on how to use the API, see the EMF Compare FAQ.