Comments (9)
Background
This is happening because Jackson doesn't have any information about what types you want. So it uses default representations to provide a best-effort mapping with the most functionality it can.
scala> val map1 = objectMapper.readValue("""{"test":"113123","myList":[{"test2":"321323"},{"test3":"11122"}]}""", classOf[Map[String,Any]])
map1: Map[String,Any] = Map(test -> 113123, myList -> [{test2=321323}, {test3=11122}])
scala> map1("myList").getClass
res4: java.lang.Class[_] = class java.util.ArrayList
scala> val list = map1("myList").asInstanceOf[java.util.ArrayList[Any]]
list: java.util.ArrayList[Any] = [{test2=321323}, {test3=11122}]
scala> list.get(0).getClass
res6: java.lang.Class[_] = class java.util.LinkedHashMap
scala> val map2 = list.get(0).asInstanceOf[java.util.LinkedHashMap[String, Any]]
map2: java.util.LinkedHashMap[String,Any] = {test2=321323}
Core Jackson is selecting the types here, not the Scala module, specifically through a class called UntypedObjectDeserializer
.
Workarounds
Use JavaConverters
Without tweaking your Jackson configuration, you can always add another layer of conversion when you type match the list:
scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._
scala> val list = map1("myList") match { case list: java.util.ArrayList[_] => list.asScala }
list: scala.collection.mutable.Buffer[_] = Buffer({test2=321323}, {test3=11122})
scala> val map2 = list(0) match { case map: java.util.Map[_, _] => map.asScala }
map2: scala.collection.mutable.Map[_, _] = Map(test2 -> 321323)
Set USE_JAVA_ARRAY_FOR_JSON_ARRAY
You can alter your deserialization configuration to tell Jackson to use Array[Object]
instead of java.util.ArrayList[Object]
, which although it's not a List, would at least provide IndexedSeq semantics for the result.
scala> import org.codehaus.jackson.map.DeserializationConfig
import org.codehaus.jackson.map.DeserializationConfig
scala> objectMapper.configure(DeserializationConfig.Feature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true)
res8: org.codehaus.jackson.map.ObjectMapper = org.codehaus.jackson.map.ObjectMapper@54ede19e
scala> val map1 = objectMapper.readValue("""{"test":"113123","myList":[{"test2":"321323"},{"test3":"11122"}]}""", classOf[Map[String,Any]])
map1: scala.collection.immutable.Map[String, Any] = Map(test -> 113123, myList -> Array({test2=321323}, {test3=11122}))
Declare a case class to use as a value mapping
The most robust solution in the current codebase is to declare a case class that maps your values to the types you want. This gives Jackson the opportunity to directly construct the types of your data.
scala> case class Foo(test: String, myList: List[Map[String, String]])
defined class Foo
scala> val foo = objectMapper.readValue("""{"test":"113123","myList":[{"test2":"321323"},{"test3":"11122"}]}""", classOf[Foo])
foo: Foo = Foo(113123,List(Map(test2 -> 321323), Map(test3 -> 11122)))
scala> foo.test
res10: String = 113123
scala> foo.myList
res11: List[Map[String,String]] = List(Map(test2 -> 321323), Map(test3 -> 11122))
Solutions
If you have a strong use case for why you'd want to change the default mapping used by UntypedObjectDeserializer
, a future version of the Scala module could implement an AnyDeserializer
that would take its place. This wouldn't be terribly hard to implement and would probably add value, even if it's just for querying with the REPL. I'd be interested in hearing any other specific examples for how this might help you.
from jackson-module-scala.
I think the idea is exactly to use one of Scala List/Map types for contained JSON arrays, lists, but I only now realized that since use of Scala module does not necessarily imply all-Scala, it can't by default change mapping that is used by UntypedObjectDeserializer.
However, maybe I should think of ways to make behavior of UntypedObjectDeserializer configurable, so that it would be possible to force use of Scala types (or any other objects).
from jackson-module-scala.
Chris, thinking about this bit more, perhaps there should just be an option to override UntypedObjectDeserializer to use Scala Lists and Maps? This is legal (they are Objects after all). And should be easy to configure as well; just need a setting in module, and it could register handler for Object.class
.
from jackson-module-scala.
That's probably what I'll end up doing, and probably as the default. Most users are going to want to minimize their exposure to Java standard types anyway, and it looks like UntypedObjectDeserializer
will be straightforward to derive from.
from jackson-module-scala.
Makes sense to me.
from jackson-module-scala.
@JamiePullar, a follow-up question: is your desire to just see the returned types be Scala types, or do you want specific control over what the default types should be? In other words, for JSON arrays, must it be a List, or would any Seq be OK?
from jackson-module-scala.
In my particular case I needed a List, however I would think a seq could be made to work. Specific control does sound cool though..
from jackson-module-scala.
For efficiency reasons, I've implemented this using JavaConverters
under the hood. Since the Jackson default is an ArrayList
, the conversion selects mutable.Buffer
as the closest match. This is in the recently released version 1.9.3 (see a369660).
The code also respects the USE_JAVA_ARRAY_FOR_JSON_ARRAY
deserialization feature, giving you three different options (you can revert to the original ArrayList
behavior by composing a custom Jackson module that omits the UntypedObjectDeserializerModule
), which I think at this point should be sufficient. If you need more specific mappings then I would recommend using case classes.
from jackson-module-scala.
I have finally had the chance to implement these changes, I got a nearly
1/3 performance increase, and used a custom Deserializer:
private class NestedTypeObjectDeserializer extends
JacksonUntypedObjectDeserializer {
override def mapArray(jp: JsonParser, ctxt: DeserializationContext):
AnyRef =
super.mapArray(jp, ctxt).asInstanceOf[ArrayList[AnyRef]].asScala.toList
override def mapObject(jp: JsonParser, ctxt: DeserializationContext):
AnyRef =
super.mapObject(jp, ctxt).asInstanceOf[LinkedHashMap[String,
AnyRef]].asScala.toMap
}
Thank you very much for your time on this.
On Thu, Mar 8, 2012 at 4:09 PM, Christopher Currie <
[email protected]
wrote:
For efficiency reasons, I've implemented this using
JavaConverters
under
the hood. Since the Jackson default is anArrayList
, the conversion
selectsmutable.Buffer
as the closest match. This is in the recently
released version 1.9.3 (see a369660).The code also respects the
USE_JAVA_ARRAY_FOR_JSON_ARRAY
deserialization
feature, giving you three different options (you can revert to the original
ArrayList
behavior by composing a custom Jackson module that omits the
UntypedObjectDeserializerModule
), which I think at this point should be
sufficient. If you need more specific mappings then I would recommend using
case classes.
Reply to this email directly or view it on GitHub:
from jackson-module-scala.
Related Issues (20)
- support deserializing Scala Iterators HOT 1
- Release 2.15.2 HOT 2
- Dependency on .class and .tasty files causes footprint bloat for GraalVM NativeImage HOT 5
- Failure to deserialise scala Map when default typing is activated HOT 10
- DefaultScalaModule breaks deserialization of java classes HOT 13
- 2.16 jackson-databind deserialization changes have broken support for the scala.util.Either class HOT 1
- Collection of case classes deserialized as Collection of Map2 HOT 4
- Release 2.15.3 HOT 4
- Release 2.16.0-rc1 HOT 1
- error when deserializing yaml HOT 2
- Release 2.16.0 HOT 2
- release jackson-module-scala v2.12 for Scala 2.10 HOT 2
- Release 2.16.1 HOT 1
- Update `2.17` branch to `2.17.0-rc1-SNAPSHOT` HOT 2
- Release 2.15.4 HOT 1
- Release 2.17.0-rc1 HOT 1
- Release 2.16.2 HOT 2
- Release 2.17.0 HOT 2
- Performance regression with 2.17.x HOT 32
- Update 2.17 release note wiki with Scala module 2.17 fixes HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from jackson-module-scala.