Scala / Play: анализ JSON в карту вместо JsObject

Аргументы командной строки сохраняются как строки в массиве String String[] args that is passed to main () `.

java [program name] [arg1,arg2 ,..]

Аргументы командной строки - это входы, которые принимают из командной строки при запуске программы , Пропускаемые аргументы могут быть любыми. Который хранится в массиве args[].

//Display all command line information
         class ArgDemo{
            public static void main(String args[]){
            System.out.println("there are "+args.length+"command-line arguments.");
            for(int i=0;i<args.length;i++)
            System.out.println("args["+i+"]:"+args[i]);
    }
    }

Пример:

java Argdemo one two

Выход будет:

there are 2 command line arguments:
they are:
arg[0]:one
arg[1]:two
23
задан Caballero 17 November 2013 в 19:03
поделиться

5 ответов

Я решил использовать модуль Джексона для Scala .

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

val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
val obj = mapper.readValue[Map[String, Object]](jsonString)
26
ответ дан Caballero 17 November 2013 в 19:03
поделиться

Scala в целом не рекомендует использовать downcasting, и Play Json в этом отношении идиоматичен. Приведение в исходное состояние является проблемой, поскольку компилятор не может отследить возможность неправильного ввода или других ошибок. Получив значение типа Map[String, Any], вы сами - компилятор не может помочь вам отследить, какими могут быть эти значения Any.

У вас есть пара альтернатив. Первый - использовать операторы пути для перехода к определенной точке дерева, где вы знаете тип:

scala> val json = Json.parse(jsonString)
json: play.api.libs.json.JsValue = {"key1": ...

scala> val k1Value = (json \ "key1" \ "subkey1" \ "k1").validate[String]
k1Value: play.api.libs.json.JsResult[String] = JsSuccess(value1,)

Это похоже на что-то вроде следующего:

val json: Map[String, Any] = ???

val k1Value = json("key1")
  .asInstanceOf[Map[String, Any]]("subkey1")
  .asInstanceOf[Map[String, String]]("k1")

Но прежний подход имеет то преимущество, что он терпит неудачу способами, которые легче рассуждать. Вместо потенциально трудного для интерпретации ClassCastException исключения , мы просто получили бы хорошее значение JsError .

Обратите внимание, что мы можем проверить в точке выше в дереве, если мы знаем, какую структуру мы ожидаем:

scala> println((json \ "key2").validate[List[Map[String, String]]])
JsSuccess(List(Map(j1 -> v1, j2 -> v2), Map(j1 -> x1, j2 -> x2)),)

Оба эти примера Play основаны на концепции классы типов - и, в частности, в экземплярах классов типов Read, предоставляемых Play. Вы также можете предоставить свои собственные экземпляры классов типов для типов, которые вы определили сами. Это позволит вам сделать что-то вроде следующего:

val myObj = json.validate[MyObj].getOrElse(someDefaultValue)

val something = myObj.key1.subkey1.k2(2)

Или что угодно. Документация Play (ссылка выше) дает хорошее представление о том, как это сделать, и вы всегда можете задать дополнительные вопросы здесь, если у вас возникнут проблемы.


Чтобы обратиться к обновлению в вашем вопросе, можно изменить вашу модель, чтобы учесть различные возможности для key2, а затем определить свой собственный экземпляр Reads:

case class MyJson(key1: String, key2: Either[String, Map[String, String]])

implicit val MyJsonReads: Reads[MyJson] = {
  val key2Reads: Reads[Either[String, Map[String, String]]] =
    (__ \ "key2").read[String].map(Left(_)) or
    (__ \ "key2").read[Map[String, String]].map(Right(_))

  ((__ \ "key1").read[String] and key2Reads)(MyJson(_, _))
}

Какой работает так:

scala> Json.parse(jsonString).as[List[MyJson]].foreach(println)
MyJson(v1,Left(v2))
MyJson(x1,Left(x2))
MyJson(y1,Right(Map(subkey1 -> subval1, subkey2 -> subval2)))

Да, это немного более многословно, но это предварительное многословие, за которое вы платите один раз (и это дает вам хорошие гарантии), а не кучу приведений это может привести к запутанным ошибкам во время выполнения.

Это не для всех, и, возможно, вам не по вкусу - это прекрасно. Вы можете использовать операторы пути для обработки подобных случаев или даже простого старого Джексона. Я бы посоветовал вам дать шанс подходу типа «класс», хотя есть крутая кривая обучения, но многие люди (включая меня) очень сильно это предпочитают.

31
ответ дан Travis Brown 17 November 2013 в 19:03
поделиться

Для дальнейшего ознакомления и в духе простоты вы всегда можете перейти к:

Json.parse(jsonString).as[Map[String, JsValue]]

Однако это вызовет исключение для строк JSON, не соответствующих формату (но я предполагаю, что это относится к подход Джексона также). JsValue теперь можно обрабатывать следующим образом:

jsValueWhichBetterBeAList.as[List[JsValue]]

Я надеюсь, что разница между обработкой Object и JsValue с не является для вас проблемой (только потому, что вы жаловались на JsValue является собственностью). Очевидно, это немного похоже на динамическое программирование на типизированном языке, который обычно не подходит (ответ Трэвиса - это обычно путь), но иногда это приятно, я думаю.

10
ответ дан Dominik Bucher 17 November 2013 в 19:03
поделиться

Вы можете просто извлечь значение Json, и Scala даст вам соответствующую карту. Пример:

   var myJson = Json.obj(
          "customerId" -> "xyz",
          "addressId" -> "xyz",
          "firstName" -> "xyz",
          "lastName" -> "xyz",
          "address" -> "xyz"
      )

Предположим, у вас есть Json вышеупомянутого типа. Чтобы преобразовать его в карту, просто сделайте:

var mapFromJson = myJson.value

Это даст вам карту типа: scala.collection.immutable.HashMap $ HashTrieMap

0
ответ дан Pushp Raj Saurabh 17 November 2013 в 19:03
поделиться

Рекомендовал бы ознакомиться с сопоставлением с образцом и рекурсивными ADT в целом, чтобы лучше понять, почему Play Json рассматривает JSON как «гражданина первого класса».

При этом многие API-интерфейсы на основе Java (например, библиотеки Google Java) ожидают десериализации JSON как Map[String, Object]. Хотя вы можете очень просто создать свою собственную функцию, которая рекурсивно генерирует этот объект с сопоставлением с шаблоном, простейшим решением, вероятно, будет использование следующего существующего шаблона:

import com.google.gson.Gson
import java.util.{Map => JMap, LinkedHashMap}

val gson = new Gson()

def decode(encoded: String): JMap[String, Object] =   
   gson.fromJson(encoded, (new LinkedHashMap[String, Object]()).getClass)

LinkedHashMap используется, если вы хотите сохранить порядок ключей во время десериализации (HashMap может использоваться, если порядок не имеет значения). Полный пример здесь .

0
ответ дан Matt 17 November 2013 в 19:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: