Jakiej biblioteki JSON użyć w Scali? [Zamknięte]

125

Muszę zbudować ciąg JSON, coś takiego:

[
  { 'id': 1, 'name': 'John'},
  { 'id': 2, 'name': 'Dani'}
]

val jArray = JsArray();
jArray += (("id", "1"), ("name", "John"))
jArray += (("id", "2"), ("name", "Dani"))
println(jArray.dump)

Muszę mieć możliwość dodawania wierszy do jArray, na przykładjArray += ...

Jaka jest najbliższa biblioteka / rozwiązanie tego problemu?

David Portabella
źródło

Odpowiedzi:

219

Niestety pisanie biblioteki JSON jest wersją kodowania aplikacji z listą zadań do wykonania przez społeczność Scala.

Istnieje wiele alternatyw. Podaję je w przypadkowej kolejności, z uwagami:

  1. parsing.json.JSON - Uwaga, ta biblioteka jest dostępna tylko do wersji Scala 2.9.x (usunięta w nowszych wersjach)
  2. spray-json - wyodrębniony z projektu Spray
  3. Jerkson ± - Ostrzeżenie przed ładną biblioteką (zbudowaną na bazie Java Jackson), ale teraz porzuć oprogramowanie. Jeśli masz zamiar tego użyć, prawdopodobnie postępuj zgodnie z przykładem projektu Scalding i użyj rozwidlenia backchat.io
  4. sjson - przez Debasish Ghosh
  5. lift-json - może być używany niezależnie od projektu Lift
  6. json4s 💣 § ± - Ekstrakcja z lift-json, która próbuje utworzyć standardowy JSON AST, z którego mogą korzystać inne biblioteki JSON. Zawiera implementację wspieraną przez Jacksona
  7. Argonaut 💣 § - Zorientowana na FP biblioteka JSON dla Scala, od ludzi stojących za Scalaz
  8. play-json ± - Teraz dostępny jako samodzielny, zobacz tę odpowiedź, aby uzyskać szczegółowe informacje
  9. dijon - Poręczna, bezpieczna i wydajna biblioteka JSON, używa jsoniter-scala pod maską.
  10. sonofjson - biblioteka JSON, której celem jest super proste API
  11. Jawn - biblioteka JSON autorstwa Erika Osheima, której celem jest osiągnięcie szybkości Jacksona lub większej
  12. Rapture JSON ± - front-end JSON, który może używać 2, 4, 5, 6, 7, 11 lub Jacksona jako zaplecza
  13. circe 💣 - widelec z Argonaut zbudowany na szczycie kotów zamiast scalaz
  14. jsoniter-scala - makra Scala do generowania w czasie kompilacji ultraszybkich kodeków JSON
  15. jackson-module-scala - Moduł dodatkowy dla Jacksona do obsługi typów danych specyficznych dla Scali
  16. borer - Wydajna serializacja CBOR i JSON (de) w Scali

💣 = nie ma naprawionych luk w zabezpieczeniach, § = ma integrację ze Scalaz, ± = obsługuje współpracę z Jacksonem JsonNode

W Snowplow używamy json4s z zapleczem Jacksona; mieliśmy też dobre doświadczenia z Argonautą.

Alex Dean
źródło
8
To nieprawda, że ​​lift-json jest częścią większego projektu LIft, możesz po prostu polegać na lift-json i nic więcej z projektu Lift nie trafi do twojego projektu.
fmpwizard
3
@AlexDean: Co jest takiego złego w parsing.json.JSON?
Matthias Braun
Wygląda na to, że play-json zostanie wydany wraz z Play 2.2 i możesz już z niego korzystać: mandubian.com/2013/02/21/play-json-stand-alone
Christiaan
2
@BjornTipling - dobra uwaga, nie mogę znaleźć żadnej wzmianki o tym, że jest przestarzała w 2.11. Usunięto ten komentarz
Alex Dean
2
Lista powinna umieścić jackson-module-scala na szczycie, który ma zdecydowanie najlepsze pod względem wydajności, prostoty, konserwacji i wsparcia.
lyomi
17

Lift-json jest w wersji 2.6 i działa bardzo dobrze (a także jest bardzo dobrze obsługiwany, opiekun jest zawsze gotowy do naprawienia wszelkich błędów, które mogą znaleźć użytkownicy. Przykłady użycia go można znaleźć w repozytorium github

Opiekun (Joni Freeman) jest zawsze osiągalny na liście mailingowej Lift . Na liście mailingowej są również inni użytkownicy, którzy również są bardzo pomocni.

Jak wskazuje @Alexey, jeśli chcesz używać biblioteki z inną wersją Scali, powiedz 2.11.x, zmień scalaVersioni użyj %%w następujący sposób:

scalaVersion := "2.11.5" 

"net.liftweb" %% "lift-json" % "2.6"

Możesz sprawdzić witrynę liftweb.net, aby w miarę upływu czasu sprawdzać najnowszą wersję.

fmpwizard
źródło
3
Używam również lift-json i mogę ręczyć, że jest to świetna biblioteka. To sprawia, że ​​zarówno analizowanie, jak i generowanie / serializowanie JSON jest bardzo łatwe.
Dan Simon
1
+1 dla "net.liftweb"% "lift-json_2.10"% "2.5.1"
Dylan Hogg,
2
a dla Scala 2.11: "net.liftweb"% "lift-json_2.11"% "2.6-M4"
Alexey,
15

Sugeruję użycie jerksona , obsługuje większość podstawowych konwersji typu:

scala> import com.codahale.jerkson.Json._

scala> val l = List( 
                 Map( "id" -> 1, "name" -> "John" ),
                 Map( "id" -> 2, "name" -> "Dani")
               )

scala> generate( l )

res1: String = [{"id":1,"name":"John"},{"id":2,"name":"Dani"}]
paradygmatyczny
źródło
2
Ma również naprawdę świetne wsparcie dla klas przypadków, które mogą zapewnić bardzo elegancką i bezpieczną obsługę JSON.
Thomas Lockney
9
Ta biblioteka została opuszczona przez autora, czy jest jakaś alternatywa?
zjffdu,
1
Nie zapominajmy o rapture.io , który „jest rodziną bibliotek Scala udostępniających piękne idiomatyczne interfejsy API Scala do typowych zadań programistycznych, takich jak praca z I / O, kryptografia oraz przetwarzanie JSON i XML”.
Piohen
12

Numer 7 na liście to Jackson, który nie używa Jerkson. Obsługuje obiekty Scala (klasy przypadków itp.).

Poniżej znajduje się przykład tego, jak go używam.

object MyJacksonMapper extends JacksonMapper
val jsonString = MyJacksonMapper.serializeJson(myObject)
val myNewObject = MyJacksonMapper.deserializeJson[MyCaseClass](jsonString)

To sprawia, że ​​jest to bardzo proste. Ponadto XmlSerializer i obsługa adnotacji JAXB jest bardzo przydatna.

Ten wpis na blogu opisuje jego użycie z adnotacjami JAXB i strukturą Play.

http://krasserm.blogspot.co.uk/2012/02/using-jaxb-for-xml-and-json-apis-in.html

Oto mój obecny JacksonMapper.

trait JacksonMapper {

  def jsonSerializer = {
    val m = new ObjectMapper()
    m.registerModule(DefaultScalaModule)
    m
  }

  def xmlSerializer = {
    val m = new XmlMapper()
    m.registerModule(DefaultScalaModule)
    m
  }

  def deserializeJson[T: Manifest](value: String): T = jsonSerializer.readValue(value, typeReference[T])
  def serializeJson(value: Any) = jsonSerializer.writerWithDefaultPrettyPrinter().writeValueAsString(value)
  def deserializeXml[T: Manifest](value: String): T = xmlSerializer.readValue(value, typeReference[T])
  def serializeXml(value: Any) = xmlSerializer.writeValueAsString(value)

  private[this] def typeReference[T: Manifest] = new TypeReference[T] {
    override def getType = typeFromManifest(manifest[T])
  }

  private[this] def typeFromManifest(m: Manifest[_]): Type = {
     if (m.typeArguments.isEmpty) { m.erasure }
     else new ParameterizedType {
       def getRawType = m.erasure

       def getActualTypeArguments = m.typeArguments.map(typeFromManifest).toArray

       def getOwnerType = null
     }
  }
}   
Ramon
źródło
8

Może trochę się spóźniłem, ale naprawdę powinieneś spróbować użyć biblioteki json z frameworka odtwarzania. Możesz zajrzeć do dokumentacji . W obecnej wersji 2.1.1 nie można było używać go oddzielnie bez całej gry 2, więc zależność będzie wyglądać następująco:

val typesaferepo  = "TypeSafe Repo" at "http://repo.typesafe.com/typesafe/releases"
val play2 = "play" %% "play" % "2.1.1"

Zapewni Ci całe środowisko gry ze wszystkimi rzeczami na pokładzie.

Ale jak wiem, faceci z Typesafe mają plan, aby oddzielić to w wersji 2.2. Tak więc istnieje samodzielny play-json z wersji 2.2-snapshot.

Alex Povar
źródło
2
Do Twojej wiadomości: Biblioteka JSON Play jest już dostępna w repozytorium migawek Typesafe
Tvaroh
... które możesz dodać sposób .
bluenote10
Jest oficjalnie używany w samouczku sbt
serv-inc
5

Powinieneś sprawdzić Genson . Po prostu działa i jest znacznie łatwiejszy w użyciu niż większość istniejących alternatyw w Scali. Jest szybki, posiada wiele funkcji i integracji z innymi bibliotekami (jodatime, json4s DOM api ...).

Wszystko to bez żadnego wymyślnego niepotrzebnego kodu, takiego jak implicity, niestandardowych czytelników / pisarzy dla podstawowych przypadków, niewidoczne API z powodu przeciążenia operatora ...

Korzystanie z niego jest tak proste, jak:

import com.owlike.genson.defaultGenson_

val json = toJson(Person(Some("foo"), 99))
val person = fromJson[Person]("""{"name": "foo", "age": 99}""")

case class Person(name: Option[String], age: Int)

Zastrzeżenie: jestem autorem Gensonów, ale to nie oznacza, że ​​nie jestem obiektywny :)

eugen
źródło
Całkiem fajnie, szkoda, że ​​ma jeden problem github.com/owlike/genson/issues/82
samthebest
5

Oto podstawowa implementacja zapisu, a następnie odczytu jsonpliku za pomocą json4s.

import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.JsonDSL._
import java.io._
import scala.io.Source


object MyObject { def main(args: Array[String]) {

  val myMap = Map("a" -> List(3,4), "b" -> List(7,8))

  // writing a file 
  val jsonString = pretty(render(myMap))

  val pw = new PrintWriter(new File("my_json.json"))
  pw.write(jsonString)
  pw.close()

  // reading a file 
  val myString = Source.fromFile("my_json.json").mkString
  println(myString)

  val myJSON = parse(myString)

  println(myJSON)

  // Converting from JOjbect to plain object
  implicit val formats = DefaultFormats
  val myOldMap = myJSON.extract[Map[String, List[Int]]]

  println(myOldMap)
 }
}
Akavall
źródło
4

Jawn to bardzo elastyczna biblioteka parserów JSON w Scali. Umożliwia także generowanie niestandardowych AST; wystarczy dostarczyć mu małą cechę do zmapowania do AST.

Działał świetnie w przypadku niedawnego projektu, który wymagał trochę analizy JSON.

HRJ
źródło
4

Wydaje się, że na liście odpowiedzi brakuje Rapture. Można go pobrać ze strony http://rapture.io/ i umożliwia (między innymi):

  • wybierz zaplecze JSON, co jest bardzo przydatne, jeśli już go używasz (w imporcie)
  • zdecyduj, czy pracujesz z Try, Future, Option, Either itp. (również w imporcie)
  • wykonuj dużo pracy w jednej linii kodu.

Nie chcę kopiować / wklejać przykładów Rapture z jego strony. Miłą prezentację na temat funkcji Rapture wygłosił Jon Pretty na SBTB 2014: https://www.youtube.com/watch?v=ka5-OLJgybI

Piohen
źródło
3

Odpowiedź @ AlaxDean nr 7, Argonaut jest jedyną, której udało mi się szybko rozpocząć pracę z sbt i intellij. Właściwie json4 również zajęło trochę czasu, ale radzenie sobie z surowym AST nie jest tym, czego chciałem. Dostałem argonautę do pracy, umieszczając jedną linię w moim build.st:

libraryDependencies += "io.argonaut" %% "argonaut" % "6.0.1"

A potem prosty test, aby sprawdzić, czy uda mi się uzyskać JSON:

package mytest


import scalaz._, Scalaz._
import argonaut._, Argonaut._

object Mytest extends App {

  val requestJson  =
    """
    {
      "userid": "1"
    }
    """.stripMargin

  val updatedJson: Option[Json] = for {
    parsed <- requestJson.parseOption
  } yield ("name", jString("testuser")) ->: parsed

  val obj = updatedJson.get.obj
  printf("Updated user: %s\n", updatedJson.toString())
  printf("obj : %s\n", obj.toString())
  printf("userid: %s\n", obj.get.toMap("userid"))
}

I wtedy

$ sbt
> run
Updated user: Some({"userid":"1","name":"testuser"})
obj : Some(object[("userid","1"),("name","testuser")])
userid: "1"

Upewnij się, że znasz opcję Option, która jest po prostu wartością, która może być również zerowa (chyba zerowa bezpieczna). Argonaut używa Scalaz, więc jeśli zobaczysz coś, czego nie rozumiesz, jak symbol \/(operację), prawdopodobnie jest to Scalaz.

Bjorn
źródło
2

Możesz spróbować tego: https://github.com/momodi/Json4Scala

Jest prosty i ma tylko jeden plik scala z mniej niż 300 wierszami kodu.

Istnieją próbki:

test("base") {
    assert(Json.parse("123").asInt == 123)
    assert(Json.parse("-123").asInt == -123)
    assert(Json.parse("111111111111111").asLong == 111111111111111l)
    assert(Json.parse("true").asBoolean == true)
    assert(Json.parse("false").asBoolean == false)
    assert(Json.parse("123.123").asDouble == 123.123)
    assert(Json.parse("\"aaa\"").asString == "aaa")
    assert(Json.parse("\"aaa\"").write() == "\"aaa\"")

    val json = Json.Value(Map("a" -> Array(1,2,3), "b" -> Array(4, 5, 6)))
    assert(json("a")(0).asInt == 1)
    assert(json("b")(1).asInt == 5)
}
test("parse base") {
    val str =
        """
          {"int":-123, "long": 111111111111111, "string":"asdf", "bool_true": true, "foo":"foo", "bool_false": false}
        """
    val json = Json.parse(str)
    assert(json.asMap("int").asInt == -123)
    assert(json.asMap("long").asLong == 111111111111111l)
    assert(json.asMap("string").asString == "asdf")
    assert(json.asMap("bool_true").asBoolean == true)
    assert(json.asMap("bool_false").asBoolean == false)
    println(json.write())
    assert(json.write().length > 0)
}
test("parse obj") {
    val str =
        """
           {"asdf":[1,2,4,{"bbb":"ttt"},432]}
        """
    val json = Json.parse(str)
    assert(json.asMap("asdf").asArray(0).asInt == 1)
    assert(json.asMap("asdf").asArray(3).asMap("bbb").asString == "ttt")
}
test("parse array") {
    val str =
        """
           [1,2,3,4,{"a":[1,2,3]}]
        """
    val json = Json.parse(str)
    assert(json.asArray(0).asInt == 1)
    assert(json(4)("a")(2).asInt == 3)
    assert(json(4)("a")(2).isInt)
    assert(json(4)("a").isArray)
    assert(json(4)("a").isMap == false)
}
test("real") {
    val str = "{\"styles\":[214776380871671808,214783111085424640,214851869216866304,214829406537908224],\"group\":100,\"name\":\"AO4614【金宏达电子】现货库存 质量保证 欢迎购买@\",\"shopgrade\":8,\"price\":0.59,\"shop_id\":60095469,\"C3\":50018869,\"C2\":50024099,\"C1\":50008090,\"imguri\":\"http://img.geilicdn.com/taobao10000177139_425x360.jpg\",\"cag\":50006523,\"soldout\":0,\"C4\":50006523}"
    val json = Json.parse(str)
    println(json.write())
    assert(json.asMap.size > 0)
}
momodi
źródło
Podoba mi się to - doskonałe do małych przypadków użycia - nie potrzeba żadnych bibliotek.
Samik R
2

Używam uPickle, który ma tę dużą zaletę, że automatycznie obsługuje zagnieżdżone klasy przypadków:

object SerializingApp extends App {

  case class Person(name: String, address: Address)

  case class Address(street: String, town: String, zipCode: String)

  import upickle.default._

  val john = Person("John Doe", Address("Elm Street 1", "Springfield", "ABC123"))

  val johnAsJson = write(john)
  // Prints {"name":"John Doe","address":{"street":"Elm Street 1","town":"Springfield","zipCode":"ABC123"}}
  Console.println(johnAsJson)

  // Parse the JSON back into a Scala object
  Console.println(read[Person](johnAsJson))  
}

Dodaj to do swojego, build.sbtaby używać uPickle:

libraryDependencies += "com.lihaoyi" %% "upickle" % "0.4.3"
Matthias Braun
źródło
0

Używam biblioteki PLAY JSON, możesz znaleźć repozytorium mavn tylko dla biblioteki JSON, a nie całego frameworka tutaj

    val json = "com.typesafe.play" %% "play-json" % version
    val typesafe = "typesafe.com" at "http://repo.typesafe.com/typesafe/releases/"

Bardzo dobre samouczki dotyczące ich używania są dostępne tutaj:

http://mandubian.com/2012/09/08/unveiling-play-2-dot-1-json-api-part1-jspath-reads-combinators/

http://mandubian.com/2012/10/01/unveiling-play-2-dot-1-json-api-part2-writes-format-combinators/

http://mandubian.com/2012/10/29/unveiling-play-2-dot-1-json-api-part3-json-transformers/

Montaro
źródło
JSON Play był już wspomniany powyżej.
bluenote10
0

Pozwólcie, że podam również wersję SON of JSON :

import nl.typeset.sonofjson._

arr(
  obj(id = 1, name = "John)
  obj(id = 2, name = "Dani)
)
Wilfred Springer
źródło
Bardzo chciałbym tego użyć, ale nie mogę wymyślić, jak dodać to do moich zależności, ponieważ nie ma tego w Maven.
Jason Wolosonovich
0

Play wydał swój moduł do obsługi JSON niezależnie od Play Framework, Play WS

Opublikowałem na ten temat post na blogu, sprawdź to na http://pedrorijo.com/blog/scala-json/

Używając klas przypadków i Play WS (już zawartych w Play Framework), konwertujesz przypadki między klasami json i case z prostą niejawną jednowierszową

case class User(username: String, friends: Int, enemies: Int, isAlive: Boolean)

object User {
  implicit val userJsonFormat = Json.format[User]
}
pedrorijo91
źródło