Skip to content

@JsonManagedReference doesn't work on Scala's collections #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ImNotMyself opened this issue Jun 25, 2015 · 4 comments
Open

@JsonManagedReference doesn't work on Scala's collections #210

ImNotMyself opened this issue Jun 25, 2015 · 4 comments

Comments

@ImNotMyself
Copy link

Sample code snippet

class Foo(val f:Int){
  @JsonManagedReference val children: Seq[Bar] = null
}

class Bar(val b: Int) {
  @JsonBackReference val parent: Foo = null
}

REPL:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
import org.Foo

val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
mapper.readValue( """{"f":1,"children":[{"b":2}]}""", classOf[Foo])

Exception occuried:

com.fasterxml.jackson.databind.JsonMappingException: Unsupported container type (scala.collection.immutable.$colon$colon) when resolving reference 'defaultReference' (through reference chain: org.Foo["children"])
  at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210)
  at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:177)
  at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1475)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:222)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:399)
  at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1100)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:294)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:131)
  at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3707)
  at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2719)
  ... 35 elided
Caused by: java.lang.IllegalStateException: Unsupported container type (scala.collection.immutable.$colon$colon) when resolving reference 'defaultReference'
  at com.fasterxml.jackson.databind.deser.impl.ManagedReferenceProperty.setAndReturn(ManagedReferenceProperty.java:135)
  at com.fasterxml.jackson.databind.deser.impl.ManagedReferenceProperty.set(ManagedReferenceProperty.java:111)
  at com.fasterxml.jackson.databind.deser.impl.ManagedReferenceProperty.deserializeAndSet(ManagedReferenceProperty.java:100)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:220)
  ... 41 more

root cause

ManagedReferenceProperty.setAndReturn @ jackson-databind try to populate the parent instance to all its children but only cover the container types of Java Array, Collection and Map.

Since almost all the Scala collections have not implemented Java Collection or Map interface, like the Seq here in the sample, the exception thrown.

    @Override
    public Object setAndReturn(Object instance, Object value) throws IOException
    {
        /* 04-Feb-2014, tatu: As per [#390], it may be necessary to switch the
         *   ordering of forward/backward references, and start with back ref.
         */
        if (value != null) {
            if (_isContainer) { // ok, this gets ugly... but has to do for now
                if (value instanceof Object[]) {
                    for (Object ob : (Object[]) value) {
                        if (ob != null) { _backProperty.set(ob, instance); }
                    }
                } else if (value instanceof Collection<?>) {
                    for (Object ob : (Collection<?>) value) {
                        if (ob != null) { _backProperty.set(ob, instance); }
                    }
                } else if (value instanceof Map<?,?>) {
                    for (Object ob : ((Map<?,?>) value).values()) {
                        if (ob != null) { _backProperty.set(ob, instance); }
                    }
                } else {
                    throw new IllegalStateException("Unsupported container type ("+value.getClass().getName()
                            +") when resolving reference '"+_referenceName+"'");
                }
            } else {
                _backProperty.set(value, instance);
            }
        }
        // and then the forward reference itself
        return _managedProperty.setAndReturn(instance, value);
    }

In fact, I was wondering where should I raise this issue, to jackson-module-scala or jackson-databind ? I don't know but please allow me to paste it here firstly since you guys may be more familiar with Scala.

Anyway, hope that my analyze can help and the issue can be fixed by FasterXML in later release:)

@christophercurrie
Copy link
Member

This is the correct place for this issue. The Scala module will need to add support for @JsonManagedReference, which was a feature I wasn't aware of. We'll see if can be added for 2.6.

@cowtowncoder
Copy link
Member

Not sure if it could help but Scala collections are considered "collection-like" (and Maps "map-like"), via JavaType, so at least some level of detection does occur.
I wonder if there are interfaces like Iterable that are supported and that we could use?

@christophercurrie
Copy link
Member

None of the Scala collections implement any of the Java interfaces. If databind could use the "collection-like" and "map-like" properties, rather than instanceof checks, this might work for Scala collections out of the box.

@cowtowncoder
Copy link
Member

Hmmh. Very unfortunate that Iterator or Iterable are not supported... but it is what it is.

The challenge wrt JavaType is that while it allows introspection of type properties, currently there is no functionality for accessing properties of instances of type represented. I have been thinking about this lately; for example for referential types (Optional et al) it might make sense to have an accessor in JavaType for getting referenced value (or at least determine if instance does refer to a value, absent vs present). But no such functionality has been added yet, and I am on the fence as to whether it is a good idea or not -- convenient, yes, but conceptually sound? Not sure yet.
In this case it would be a good thing, regardless, as MapLikeType and CollectionLikeType could offer methods for accessing some sort of shared iterator to use.
In fact, that could even allow standard baseline serializers for such types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants