eclipse-emfcloud / emfjson-jackson

emfjson-jackson
Other
16 stars 15 forks source link

Error occurs when using EMFJSON-Jackson but not when using a non-EMF POJO and straight Jackson. #45

Closed threadedblue closed 2 years ago

threadedblue commented 2 years ago

I believe we have. a bug in EMFJSON-Jackson. Things work when processing the same JSON using Jackson and congruous non-EMF POJOs.

I can explain the situation further but I am afraid of overloading the explanation and clouding the issue. I am hoping to spark some interaction with someone who is knowledgeable. The resolution of this issue is important to me and I am willing put in some effort.

The error message:

Cannot deserialize value of type com.fasterxml.jackson.databind.JsonNode from Array value (token JsonToken.END_ARRAY) at [Source: UNKNOWN; line: -1, column: -1

The JSON:

{
    "address": [
      {
                "line": [
                    "242 GREEN STREET"
                 ]
      }
    ],
    "resourceType": "Organization",
    "id": "e44f438a-601b-30fb-85eb-bfaf8ddcb5ad"
  }

btw: I do find this comment in the Jackson code base to be puzzling and it appears to be relevant to the issue.

            // These states cannot be mapped; input stream is
            // off by an event or two

        //case END_OBJECT:
        //case END_ARRAY:

The code from Jackson JsonNodeDeserializer:

    protected final JsonNode deserializeAny(JsonParser p, DeserializationContext ctxt,
            final JsonNodeFactory nodeFactory) throws IOException
    {
        switch (p.currentTokenId()) {
        case JsonTokenId.ID_END_OBJECT: // for empty JSON Objects we may point to this?
            return nodeFactory.objectNode();
        case JsonTokenId.ID_FIELD_NAME:
            return deserializeObjectAtName(p, ctxt, nodeFactory);
        case JsonTokenId.ID_EMBEDDED_OBJECT:
            return _fromEmbedded(p, ctxt, nodeFactory);
        case JsonTokenId.ID_STRING:
            return nodeFactory.textNode(p.getText());
        case JsonTokenId.ID_NUMBER_INT:
            return _fromInt(p, ctxt, nodeFactory);
        case JsonTokenId.ID_NUMBER_FLOAT:
            return _fromFloat(p, ctxt, nodeFactory);
        case JsonTokenId.ID_TRUE:
            return nodeFactory.booleanNode(true);
        case JsonTokenId.ID_FALSE:
            return nodeFactory.booleanNode(false);
        case JsonTokenId.ID_NULL:
            return nodeFactory.nullNode();

            /* Caller checks for these, should not get here ever
        case JsonTokenId.ID_START_OBJECT:
            return deserializeObject(p, ctxt, nodeFactory);
        case JsonTokenId.ID_START_ARRAY:
            return deserializeArray(p, ctxt, nodeFactory);
            */

            // These states cannot be mapped; input stream is
            // off by an event or two

        //case END_OBJECT:
        //case END_ARRAY:
        default:
        }
        return (JsonNode) ctxt.handleUnexpectedToken(handledType(), p);
    }
planger commented 2 years ago

I'm afraid I can't shed any light on this from the top of my head. Can you please also post the ecore model for which you are running into this issue?

threadedblue commented 2 years ago

Thanks for your quick response. The ecore is attached. If I can be of help, don't hesitate to task me.

fhir.ecore.zip

threadedblue commented 2 years ago

@planger

I apologize. I forgot to include two additional ecore files

namespace.ecore.zip xhtml.ecore.zip

.

threadedblue commented 2 years ago

@planger

This is quick heads up. We do have two String objects here: java.lang.String and org.hl7.fhir.String. I implemented a string deserializer to sort them out.

threadedblue commented 2 years ago

In an effort to solve my own problem, I am attempting to implement a custom ContextualDeserializer. I believe the aforementioned tale of two strings (above) is causing the problem. My current StringDeserializer is not being called when deserializing an EList. But then I get this problem:

In the method public JsonDeserializer<String> createContextual(DeserializationContext ctxt, BeanProperty property) property is null.

In EObjectFeatureProperty

public void deserializeAndSet(final JsonParser jp, final EObject current, final DeserializationContext ctxt,
      final Resource resource)

I find: final JsonDeserializer<Object> deserializer = ctxt.findContextualValueDeserializer(javaType, null); Shouldn't the above be called with a non-null BeanProperty?

threadedblue commented 2 years ago

@planger et. al.

I believe I have found the crux of the problem. My custom string deserializer ( code below ) is not being called when processing the elements of an EList<org.hl7.fhir.String) collection. It is being called at other times. e.g. Address.city, Address.state (JSON below). When the org.hl7.fhir.String is the value of a named type.

In my view EMFJSON-Jackson needs a modification. I am willing to get this done. I am becoming increasingly familiar with the code base. What ever I do, it resides, for the interim, in my own fork.

Q: Would the team like to have this fix?

I am open to any suggestions that my inform my efforts and guide me in implementing something suitable.

Perhaps the best way to put it is: Does the team want EMFJSON-Jackson to support HL7/FHIR or would they prefer the world of health information go its own way with something based on this fine body of good work.

The aforementioned JSON:

{
  "city": "Albuquerque",
  "state": "NM",
        "line": [
            "123 Applewood ln"
         ],
  "resourceType": "Address",
  "id": "e44f438a-601b-30fb-85eb-bfaf8ddcb5ad"
  }

The aforementioned StringDeserializer:

public class StringDeserializer extends JsonDeserializer<org.hl7.fhir.String> implements ContextualDeserializer {

    private JavaType valueType;

    @Override
    public StringDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) {
        JavaType type = ctxt.getContextualType() != null 
                ? ctxt.getContextualType()
                : property.getMember().getType();            
            return new StringDeserializer();
    }

    @Override
    public org.hl7.fhir.String deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        org.hl7.fhir.String s = FhirFactory.eINSTANCE.createString();
        s.setValue(p.getValueAsString());

        return s;
    }
}
threadedblue commented 2 years ago

My fix is working for now. Closing this issue.