Lift-Json - Json Ordering of Type Hints

103 views
Skip to first unread message

eggman

unread,
Oct 14, 2011, 2:42:02 PM10/14/11
to Lift
I just discovered that when doing deserialization using Lift json the
order of the json matters. More specifically the order of the
jsonClass entry matters. So the following shows 3 examples. The
first two work to deserialize, the third does not. I would like to
know if there is a way to change a setting or something so that this
ordering doesn't matter.

I am using lift-json_2.9.0-2.4-M4.jar

trait Base
case class Child(p1:String, p2:Int) extends Base

implicit val formats =
Serialization.formats(ShortTypeHints(List(classOf[Child])))

// This works
println(Serialization.read[Base]("""
{
"jsonClass":"Child",
"p1":"Test",
"p2":1
}"""))

// This works
println(Serialization.read[Base]("""
{
"jsonClass":"Child",
"p2":1,
"p1":"Test"
}"""))

// This fails
// net.liftweb.json.MappingException: No constructor for type
interface Base...
println(Serialization.read[Base]("""
{
"p2":1,
"p1":"Test",
"jsonClass":"Child"
}"""))

Joni Freeman

unread,
Oct 15, 2011, 2:03:34 AM10/15/11
to lif...@googlegroups.com
Hi,

Currently it is a required that 'jsonClass' field is the first field of
an object. Does that pose a problem in your use case?

Cheers Joni

eggman

unread,
Oct 17, 2011, 10:06:53 AM10/17/11
to Lift
It does pose a bit of a problem. As I understand it JSON can appear
in any order. It is by definition unordered. We are using other
tools in other applications to generate JSON which we are then trying
to parse in our application using Lift-Json. However the external
tools don't always give us the same order.

As a quick example, one technology we use is MongoDB. MongoDB does
not guarantee the order of the JSON that it returns. This means that
the jsonClass may appear first, last or anywhere in between. This
obviously doesn't work for what we are trying to do.

Joni Freeman

unread,
Oct 18, 2011, 2:32:48 AM10/18/11
to lif...@googlegroups.com
I see. Could you please open a ticket for this and I'll get it fixed.
The reason why originally just the first field was examined is
performance. I'll do some benchmarking to see how much this will affect.
If there is performance penalty, proper optimizations will be
implemented.

Cheers Joni


On Monday, October 17, 2011 5:06:53 PM UTC+3, eggman wrote:
It does pose a bit of a problem.  As I understand it JSON can appear
in any order.  It is by definition unordered.  We are using other
tools in other applications to generate JSON which we are then trying
to parse in our application using Lift-Json.  However the external
tools don't always give us the same order.

As a quick example, one technology we use is MongoDB.  MongoDB does
not guarantee the order of the JSON that it returns.  This means that
the jsonClass may appear first, last or anywhere in between.  This
obviously doesn't work for what we are trying to do.

Antonio Salazar Cardozo

unread,
Oct 18, 2011, 2:23:20 PM10/18/11
to lif...@googlegroups.com
As a side note, lift-mongodb already properly pulls out jsonClass and puts it at the beginning on top-level objects. We use a dash of preprocessing for embedded objects:

    override def create(in:JObject)(implicit formats:Formats) : User = {
      def typeHintExtractorForField(field:String) : PartialFunction[JValue, JValue] = {
        case JField(`field`, JArray(values)) =>
          JField(field,
            JArray(values.collect {
              case value:JObject =>
                (for (JField(formats.typeHintFieldName, JString(typeHint)) <- value) yield typeHint) match {
                  case List(typeHint) =>
                    (formats.typeHintFieldName -> typeHint) ~ value.asInstanceOf[JObject]
                  case _ =>
                    in
              }
              case other => other
            }))
      }
      val updatedIn =
        in.transform(
          typeHintExtractorForField("achievements") orElse
          typeHintExtractorForField("repeatableXpEvents") orElse {
            case other => other
          }
        ).asInstanceOf[JObject]

      super.create(updatedIn)(formats)
    }

This particular code block appropriately throws the type hint field at the beginning for the achievements and repeatableXpEvents fields of the parent object.
Thanks,
Antonio

eggman

unread,
Oct 19, 2011, 10:12:16 AM10/19/11
to Lift
Thanks for the help...I have tried to enter a ticket but after logging
in I don't seem to have that option. There is no "New Ticket" button
as shown in the tutorial video. Not sure what is going on there but
it looks like I can't enter the ticket.

eggman

unread,
Oct 19, 2011, 10:14:38 AM10/19/11
to Lift
Thanks, that is good to know. We are not using Lift MongoDB but even
if we were we also have problems with Json that is being generated
using Python tools as well. So this issue is not limited to MongoDB
it is just the first example that came to mind.

On Oct 18, 12:23 pm, Antonio Salazar Cardozo <savedfastc...@gmail.com>
wrote:

Diego Medina

unread,
Oct 19, 2011, 10:22:23 AM10/19/11
to lif...@googlegroups.com

Diego
Sent from my android cell


On Oct 19, 2011 10:12 AM, "eggman" <wadedwal...@gmail.com> wrote:
>
> Thanks for the help...I have tried to enter a ticket but after logging
> in I don't seem to have that option.  There is no "New Ticket" button

You need to be a watcher if the lift workspace

Regards

> --
> Lift, the simply functional web framework: http://liftweb.net
> Code: http://github.com/lift
> Discussion: http://groups.google.com/group/liftweb
> Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

Joni Freeman

unread,
Oct 19, 2011, 12:22:58 PM10/19/11
to lif...@googlegroups.com
Hi,

I created the ticket:

http://www.assembla.com/spaces/liftweb/tickets/1132-do-not-force--jsonclass--field-to-be-the-first-field-of-an-json-object

A fix is in trunk now.

Cheers Joni


On Wednesday, October 19, 2011 5:12:16 PM UTC+3, eggman wrote:
Thanks for the help...I have tried to enter a ticket but after logging
in I don't seem to have that option.  There is no "New Ticket" button
as shown in the tutorial video.  Not sure what is going on there but
it looks like I can't enter the ticket.

Reply all
Reply to author
Forward
0 new messages