
org.emfjson.jackson.databind.deser.EObjectDeserializer Maven / Gradle / Ivy
/*
* Copyright (c) 2015 Guillaume Hillairet.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Guillaume Hillairet - initial API and implementation
*
*/
package org.emfjson.jackson.databind.deser;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.emfjson.jackson.databind.EMFContext;
import org.emfjson.jackson.databind.property.EObjectProperty;
import org.emfjson.jackson.databind.property.EObjectPropertyMap;
import org.emfjson.jackson.databind.property.EObjectTypeProperty;
import org.emfjson.jackson.errors.JSONException;
import java.io.IOException;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static org.emfjson.jackson.databind.EMFContext.getFeature;
import static org.emfjson.jackson.databind.EMFContext.getResource;
public class EObjectDeserializer extends JsonDeserializer {
private final EObjectPropertyMap.Builder builder;
public EObjectDeserializer(EObjectPropertyMap.Builder builder) {
this.builder = builder;
}
@Override
public EObject deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
EMFContext.prepare(ctxt);
final Resource resource = getResource(ctxt);
final EStructuralFeature feature = getFeature(ctxt);
final EClass defaultType = getDefaultType(ctxt);
EObject current = null;
EObjectPropertyMap propertyMap;
if (feature == null && defaultType != null) {
propertyMap = builder.construct(ctxt, defaultType);
} else if (feature instanceof EReference) {
propertyMap = builder.construct(ctxt, ((EReference) feature).getEReferenceType());
} else {
propertyMap = builder.constructDefault(ctxt);
}
TokenBuffer buffer = null;
while (jp.nextToken() != JsonToken.END_OBJECT) {
final String field = jp.getCurrentName();
final EObjectProperty property = propertyMap.findProperty(field);
if (property instanceof EObjectTypeProperty) {
current = property.deserialize(jp, ctxt);
if (current != null) {
propertyMap = builder.construct(ctxt, current.eClass());
}
} else if (property != null && current != null) {
property.deserializeAndSet(jp, current, ctxt, resource);
} else if (property == null && current != null) {
handleUnknownProperty(jp, resource, ctxt);
} else {
if (buffer == null) {
buffer = new TokenBuffer(jp);
}
buffer.copyCurrentStructure(jp);
}
}
// handle empty objects
if (buffer == null && current == null && defaultType != null) {
return EcoreUtil.create(defaultType);
}
return buffer == null ? current: postDeserialize(buffer, current, defaultType, ctxt);
}
@Override
public EObject deserialize(JsonParser jp, DeserializationContext ctxt, EObject intoValue) throws IOException {
if (intoValue == null) {
return null;
}
EMFContext.prepare(ctxt);
EObjectPropertyMap propertyMap = builder.construct(ctxt, intoValue.eClass());
final Resource resource = getResource(ctxt);
while (jp.nextToken() != JsonToken.END_OBJECT) {
final String field = jp.getCurrentName();
final EObjectProperty property = propertyMap.findProperty(field);
if (property != null) {
property.deserializeAndSet(jp, intoValue, ctxt, resource);
} else {
handleUnknownProperty(jp, resource, ctxt);
}
}
return intoValue;
}
private EObject postDeserialize(TokenBuffer buffer, EObject object, EClass defaultType, DeserializationContext ctxt) throws IOException {
if (object == null && defaultType == null) {
return null;
}
Resource resource = getResource(ctxt);
JsonParser jp = buffer.asParser();
JsonNode tree = jp.readValueAsTree();
jp.close();
EObjectPropertyMap propertyMap = builder.find(ctxt, defaultType, tree.fieldNames());
EObjectTypeProperty typeProperty = propertyMap.getTypeProperty();
if (typeProperty != null) {
JsonNode value = tree.get(typeProperty.getFieldName());
if (value != null) {
object = typeProperty.create(value.asText(), ctxt);
}
}
if (object == null) {
object = EcoreUtil.create(defaultType);
}
// TODO explain that
propertyMap = builder.construct(ctxt, object.eClass());
jp = buffer.asParser();
JsonToken nextToken = jp.nextToken();
while (nextToken != JsonToken.END_OBJECT && nextToken != null) {
final String field = jp.getCurrentName();
final EObjectProperty property = propertyMap.findProperty(field);
if (property != null) {
property.deserializeAndSet(jp, object, ctxt, resource);
} else {
handleUnknownProperty(jp, resource, ctxt);
}
nextToken = jp.nextToken();
}
jp.close();
buffer.close();
return object;
}
private void handleUnknownProperty(JsonParser jp, Resource resource, DeserializationContext ctxt) throws IOException {
if (resource != null && ctxt.getConfig().hasDeserializationFeatures(FAIL_ON_UNKNOWN_PROPERTIES.getMask())) {
resource.getErrors().add(new JSONException("Unknown feature " + jp.getCurrentName(), jp.getCurrentLocation()));
}
// we didn't find a feature so consume
// the field and move on
jp.nextToken();
jp.skipChildren();
}
@Override
public boolean isCachable() {
return true;
}
@Override
public Class> handledType() {
return EObject.class;
}
private EClass getDefaultType(DeserializationContext ctxt) {
final EObject parent = EMFContext.getParent(ctxt);
if (parent == null) {
EClass root = EMFContext.getRoot(ctxt);
if (root != null) {
return root;
}
} else {
final EReference reference = (EReference) getFeature(ctxt);
if (reference != null && !reference.getEReferenceType().isAbstract()) {
return reference.getEReferenceType();
}
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy