Skip to content

Getting Started

Eugen Neufeld edited this page Aug 13, 2021 · 4 revisions

EMF API

We will now see how to setup and use this library to serialize EMF models into JSON. The emf-json jackson module can be used in different ways, through the standard EMF API or through the Jackson API. We will start with the most common way for EMF developers, that is by using it with the EMF Resource API. This section assumes that you are already familiar with the Eclipse Modeling Framework, if not you can first go through these: Tutorial 1 Tutorial 2.

Setup

The emf-json jackson module integrates seamlessly with the EMF Resource API by providing a Resource Factory that can be register to the ResourceSet.

See how it is done.

import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.emfjson.jackson.resource.JsonResourceFactory;

ResourceSet resourceSet = new ResourceSetImpl();
resourceSet.getResourceFactoryRegistry()
				.getExtensionToFactoryMap()
				.put("json", new JsonResourceFactory());

And that's all there is to do.

By simply registering the JsonResourceFactory to the resourceSet, you are now able to read and write all the models, having a .json extension to their file names, in JSON.

You can register the JsonResourceFactory to any extension, or register it for more than one extension, or use it for all extensions by using * as extension.

Writing JSON

We can now create EMF Resources using the standard EMF API. For example let's create a Resource named data.json.

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;

Resource resource = resourceSet.createResource
  (URI.createFileURI("src/main/resources/data.json"));

The resourceSet will know how to create resources that can handle JSON, since we register a JsonResourceFactory for the files with a json extension. This factory will create a JsonResource for that case. A JsonResource is a special kind of Resource that implements methods to write and read JSON content.

We can now add some content to the resource. For that we need to create an object using a model factory.

User bob = DomainFactory.eINSTANCE.createUser();
bob.setId(1);
bob.setName("Bob");

And we add that object to the resource, and save the resource using the save method. This will create a file named data.json.

resource.getContents().add(bob);
resource.save(null);

If we now open the file data.json, we should see that it contains a JSON object. This JSON object is the representation of our EMF object.

It contains a field eClass that indicates the type of the object. By default the value is the URI of the object's EClass. The other fields contain the values of the object's attributes.

{
  "eClass" : "http://emfjson.org/domain#//User",
  "id": 1,
  "name" : "Bob"
}

Reading JSON

Reading a JSON file is done via the standard load method of a EMF resource. Considering the previous file data.json, we can read it's content by creating another resource, and calling the load method like this.

Resource resource = resourceSet.createResource
  (URI.createFileURI("src/main/resources/data.json"));

resource.load(null);

The content of the resource can now be access like this:

User u1 = (User) resource.getContents().get(0);

Object Mapper API

You can also use the Object Mapper API provided by the Jackson library to serialize and deserialize the content of a EMF resource or de/serialize only a single EObject.

Setup

The setup is done by registering a EMFModule instance to an ObjectMapper.

import com.fasterxml.jackson.databind.ObjectMapper;
import org.emfjson.jackson.module.EMFModule;

ObjectMapper mapper = new ObjectMapper();
EMFModule module = new EMFModule();
mapper.registerModule(module);

Alternatively you can use the static method setupDefaultMapper from EMFModule to initialize an ObjectMapper with a pre defined configuration.

import com.fasterxml.jackson.databind.ObjectMapper;
import org.emfjson.jackson.module.EMFModule;

ObjectMapper mapper = EMFModule.setupDefaultMapper();

We saw previously that the resourceSet needs a JsonResourceFactory to be register so that it knows how to handle JSON content. The JsonResourceFactory contains an ObjectMapper that is either pre-configured or can be setup by the user and pass as an argument to the JsonResourceFactory constructor.

ObjectMapper mapper = new ObjectMapper();
EMFModule module = new EMFModule();
// ...
// configure the module and the mapper here...
mapper.registerModule(module);

JsonResourceFactory factory = new JsonResourceFactory(mapper);

// obtain the mapper later like this
ObjectMapper mapper = factory.getMapper();

Writing JSON

The ObjectMapper can be use to convert any Java objects into JSON. This works as well for EMF Objects and Resources once we have setup the mapper with the EMFModule.

Considering the following resource and object, same as we did before.

Resource resource = resourceSet.createURI
  (URI.createURI("src/main/resources/data.json"));

User bob = DomainFactory.eINSTANCE.createUser();
bob.setId(1);
bob.setName("Bob");

resource.getContents().add(bob);

The resource can be serialize into JSON directly by using the mapper. For example, it can be serialize into a String by using the writeValueAsString method.

String jsonString = mapper.writeValueAsString(resource);

Or it can be converted into a JsonNode by using the valueToTree method.

JsonNode jsonNode = mapper.valueToTree(resource);

EMF Objects can also be serialize independently. For that just pass the object you want to serialize to one of the mapper write method.

String jsonString = mapper.writeValueAsString(bob);

Reading JSON

Using the mapper to parse JSON data into EObjects require some configuration. The mapper indeed needs to know what is the current resourceSet that it should use. This is done by setting attribute to the mapper reader.

In that example, we tell the mapper's reader which resourceSet to use as well as which URI should be set to the resulting resource.

JsonNode data = ...

Resource resource = mapper
  .reader()
	.withAttribute(EMFContext.Attributes.RESOURCE_SET, resourceSet)
  .withAttribute(EMFContext.Attributes.RESOURCE_URI, "src/main/resources/data.json")
	.forType(Resource.class)
	.readValue(data);

These assertions should then be verified.

assertTrue(resourceSet.getResources().contains(resource));
assertEquals(URI.createURI("src/main/resources/data.json"), resource.getURI());

Similarly it is possible to read single objects with a mapper. In the following example, we tell the mapper's reader to add the parsed object into a specific resource.

JsonNode data = ...
Resource resource = ...

User user = mapper.reader()
	.withAttribute(EMFContext.Attributes.RESOURCE, resource)
	.forType(User.class)
	.readValue(data);

This assertion should then be verified.

assertTrue(resource.getContents().contains(user));
Clone this wiki locally