Десериализация массива Json в объект Scala

#json #scala #playframework #gson #jackson

#json #scala #playframework #gson #джексон

Вопрос:

У меня возникли серьезные проблемы при попытке десериализации массива JSON в объект Scala

       [{"name":"Cool","city":"College Park","address":"806","id":1},{"name":"Mars ","city":"Durham","address":"12","id":2},{"name":"Something","city":"Raleigh 
","address":"","id":3},{"name":"test","city":"","address":"","id":5}]
 

Я пробовал gson, jerkson (оболочку jackson scala), sjson, flexjson. Ни один из них не сработал. Здесь у меня есть список клиентов. Список [клиентов].

Это самое близкое, что у меня есть:

 val array = new JsonParser().parse( customers ).getAsJsonArray()
 

Это дало мне 4 массива. Однако это, очевидно, не дало мне объекта customer. Я попробовал Jerkson.

 val array = parse[List[Customer]](customers)
 

Но я понимаю это.

 GenericSignatureFormatError occured : null
 

Я просто пытаюсь найти простой способ, как в Java.

Вот мой класс Scala.

     case class Customer(
    id : Pk[ Int ],
    name : String,
    address : Option[ String ],
    city : Option[ String ],
    state : Option[ String ],
    user_id : Int )

    object Customer extends Magic[ Customer ]( Option( "Customer" ) ) { 

    def apply( name : String, address : String, city : String, state : String, user_id : Int ) = {
        new Customer( NotAssigned, name, Some( address ), Some( city ), Some( state ), user_id )
    }

    def delete( id : Int ) = {
        SQL( "DELETE from Customer where id = {id}" ).onParams( id ).executeUpdate()
    }

}
 

Спасибо за любую помощь.

Ответ №1:

С помощью gson вы можете написать свой собственный json Reader:

 case class Customer(id: Int, name: String, address: Option[String], 
  city: Option[String], state: Option[String], user_id: Int)

object CustomerJsonReader {

   def read(in: Reader) = readCustomers(new JsonReader(in))

   def readCustomers(reader: JsonReader) = {
     val customers = new ListBuffer[Customer]
     reader.beginArray()
     while (reader.hasNext()) {
       customers  = readCustomer(reader)
     }
     reader.endArray()
     customers toList
   }

   def readCustomer(reader: JsonReader): Customer = {
     var id = 0
     var customerName = ""
     var address: Option[String] = None
     var city: Option[String] = None
     var state: Option[String] = None
     var userId = 0

     reader.beginObject()
     while (reader.hasNext()) {
       val name = reader.nextName()
       name match {
         case "id" => id = reader.nextInt()
         case "address" => address = Some(reader.nextString())
         case "state" => state = Some(reader.nextString())
         case "user_id" => userId = reader.nextInt()
         case "name" => customerName = reader.nextString()
         case "city" => city = Some(reader.nextString())
         case _ => reader.skipValue()
       }
     }
     reader.endObject()
     Customer(id, customerName, address, city, state, userId)
   }

}

val json = 
  """
  [{"name":"Cool","city":"College Park","address":"806","id":1},
  {"name":"Mars ","city":"Durham","address":"12","id":2},
  {"name":"Something","city":"Raleigh  ","address":"","id":3},
  {"name":"test","city":"","address":"","id":5}] 
  """

val customers: List[Customer] = 
  CustomerJsonReader.read(new StringReader(json))
 

Комментарии:

1. Это тоже хороший ответ. Хотя это может стать невозможным для больших объектов.

2. Вы правы, но это дает большой контроль для сопоставления с любыми типами, которые мы хотим.

3. Между тем, на заводной стороне забора выполняется «def foo = new Foo(bar: 1, baz:’hey’) как JSON», и у вас есть представление вашего объекта в формате JSON. Scala действительно, действительно нуждается в таком кратком преобразовании объекта scala в JSON. Невозможность взять произвольно сложный объектный граф и легко сериализовать в JSON делает Scala REST PITA. Возможно, lift-json или Jerkson (сопровождающий, по-видимому, в самоволке, кстати) могут помочь, но хвост виляет собакой независимо; если класс case определен в 1 строке, почему преобразование его в / из JSON занимает более 20 строк? И тогда у вас будет более 30 классов домена, весело…

Ответ №2:

Я знаю, что с gson вам понадобится Array вместо scala.List . Я бы предложил попробовать это. Вы должны использовать это с gson.Я думаю, из JSON.

Комментарии:

1. Привет, спасибо за этот ответ. На самом деле это продвинуло меня немного дальше. Проблема в том, что gson не знает, как десериализовать объект Pk. Спасибо.

2. да, gson не очень хорошо обрабатывает объявления, специфичные для scala. Id — это класс case, который принимает параметры, ничто из этого не обрабатывается очень хорошо с помощью gson. можете ли вы игнорировать поле id (пометить как переходное) или оно вам обязательно нужно в вашем сериализованном выводе? я думаю, что sjson должен быть намного лучше с обработкой scala (хотя я лично его не использовал).

3. Если и когда Gson плохо обрабатывает Scala, может быть, имеет смысл попросить авторов Gson добавить поддержку (или попробовать добавить расширение, которое это делает)? А пока используйте что-нибудь, что работает со Scala (для чего есть несколько альтернативных предложений)

Ответ №3:

Вы также можете попробовать Jerkson = Jackson Scala
, довольно простой в использовании, даже если у меня были проблемы со специальными полями JSON, содержащими «-»
Небольшое сообщение, которое я недавно видел в Twitter: http://logician.free.fr/index.php/2011/09/16/play-scala-and-json /

Комментарии:

1. На данный момент рекомендую воздержаться от этого, поскольку библиотеки перешли на Jackson 2, а Jerkson еще не обновлен. Аннотации будут конфликтовать.

Ответ №4:

Теперь я сошел с ума от этого и попробовал GSON, Lift-Json, Sjson и, наконец, Jerkson, и нашел мир с этим.

Вот как я использую его в сочетании с Play:

http://logician.eu/index.php/2011/09/16/play-scala-and-json/

http://logician.eu/index.php/2011/11/01/writing-custom-deserializers-for-jerkson/

Ответ №5:

Для этой цели я использую библиотеку json от Lift, она легко позволяет анализировать JSON и извлекать значения в классы case. Он упакован как отдельный jar, поэтому для его использования вам не нужна вся платформа lift.

 import net.liftweb.json._
import net.liftweb.json.JsonDSL._

implicit val formats = DefaultFormats

val json: String = "[{...."
val parsed: JValue = parse(json)
val customers: List[Customer] = parsed.extract[List[Customer]]
 

Просто убедитесь, что все необязательные поля определены в классе case с помощью Option . Я заметил, что в вашем коде в объектах отсутствует поле user_id, что может вызвать ошибку синтаксического анализа, если поле user_id объявлено как Int вместо Option[Int] .

Комментарии:

1. Спасибо за предложение. Я думаю, что основная проблема заключается в том, что ни одна из этих библиотек не знает, как десериализовать объект Pk из Anorm. Знаете что-нибудь об этом? Спасибо.

2. Я не знаю точно, что такое объект Pk, но вы могли бы обойти это, создав неявную функцию преобразования , что-то вроде implicit def int2Pk(i: Int): PK[Int] = ... . Scala будет использовать функцию во время компиляции для автоматической обработки преобразования.

Ответ №6:

Помимо попыток создать Jerkson (из того, что я слышал, это отличная библиотека для использования), вы также можете попробовать модуль Scala от Jackson — модули — это официальный способ расширения Jackson для работы с сторонними типами данных, а также с собственными типами данных и конструкциями других языков JVM. (это не значит, что это более официально, чем Jerkson, просто есть много полезных модулей расширения Jackson, с которыми многие разработчики не знакомы)

Проблемы с модулем Scala обсуждаются в основных списках рассылки Jackson (user@jackson.codehaus.org ); возможно, вы нашли крайний случай, который можно было бы исправить.

Ответ №7:

Я написал dsl синтаксического анализатора / валидатора, который позволяет вам явно разрешить любую проблему с удалением типа. Из коробки он обрабатывает классы case, кортежи, Option, Either, List, Map, joda DatetTime, переход к функциям, сопоставление нескольких ключей и переназначение имени ключа.

Он использует синтаксический анализатор Jackson

https://github.com/HigherState/jameson

Ответ №8:

Это довольно просто с помощью scala, библиотек воспроизведения и кода

 import play.api.libs.json.{Format, Json}
case class Customer(name:String, city:String, address:String, id:Int)
object Customer {
   implicit val jsonFormat: Format[Customer] = Json.format(Customer)
}

val jsonDef = Json.parse(<json-string>)
val customers = jsonDef.as[List[Customer]]
 

customers это список Customer объектов.