【问题标题】:Working with nested maps from a JSON string使用 JSON 字符串中的嵌套映射
【发布时间】:2011-12-05 09:07:30
【问题描述】:

给定一个像这样的 JSON 字符串:

{"Locations":
  {"list":
    [
      {"description": "some description", "name": "the name", "id": "dev123"},
      {"description": "other description", "name": "other name", "id": "dev59"}
    ]
  }
}

我想从解析上述字符串的函数中返回“id”列表。 JSON.parseFull()(来自scala.util.parsing.json)给我一个Option[Any]类型的结果。 Scala REPL 将其显示为 Some(Map(Locations -> Map(list -> List(Map(id -> dev123, ...,作为 Scala 的初学者,我对使用哪种方法感到困惑。

Scala API docs 建议“将其视为集合或 monad 并使用 map、flatMap、filter 或 foreach”。顶级元素是 Option[Any] 但是应该是 Some ,其 Map 应该包含一个键“Locations”,应该包含一个键“list”,最终是一个 List。在 Scala 中编写检索“id”的函数的惯用方式是什么?

【问题讨论】:

    标签: scala


    【解决方案1】:

    首先,你应该将 json 从 Any 转换为正确的类型:

    val json = anyJson.asInstanceOf[Option[Map[String,List[Map[String,String]]]]]
    

    然后您可以使用 map 方法从 Option 中提取 id:

    val ids = json.map(_("Locations")("list").map(_("id"))).getOrElse(List())
    

    【讨论】:

    • 我认为类型不完整 - 当我使用 anyJson.asInstanceOf[Option[Map[String,Map[String,List[Map[String,String]]]]]] 时它起作用了。它是如何工作的? asInstanceOf 是我读过的运行时演员表。我假设当给定的 Any 对象不同时它会抛出异常?顺便问一下map(_("Locations")("list")... 是什么意思?
    • 是的,asInstanceOf 会抛出异常。但是,并非总是……由于 JVM 中的类型擦除。 map(_("Locations")("list").map(_("id"))map(x => x("Locactions")("list").map(_("id"))) 方法的快捷方式map 使用给定函数转换容器的内部值(在这种情况下为Option)。给定函数接收MapMap 并从中检索值(两层深)-它是包含id 的映射列表。因此,.map(_("id")) 通过从每个元素(即Map[String,String]
    【解决方案2】:

    因为返回的结果无处不在,所以您必须进行转换。使用my earlier answers 之一:

    class CC[T] { def unapply(a:Any):Option[T] = Some(a.asInstanceOf[T]) }
    
    object M extends CC[Map[String, Any]]
    object L extends CC[List[Any]]
    object S extends CC[String]
    object D extends CC[Double]
    object B extends CC[Boolean]
    
    for {
        Some(M(map)) <- List(JSON.parseFull(jsonString))
        M(locMap) = map("Locations")
        L(list) = locMap("list")
        description <- list
        M(desc) = description
        S(id) = desc("id")
    } yield id
    // res0: List[String] = List(dev123, dev59)
    

    【讨论】:

      【解决方案3】:

      对于此类任务,您应该查看Rapture.io。我也是 scala 初学者,但从我搜索的内容来看,这似乎具有最友好的语法。这是一个简短的示例,取自gist

      import rapture.io._
      
      // Let's parse some JSON
      val src: Json = Json.parse("""
      {
        "foo": "Hello world",
        "bar": {
          "baz": 42
        }
      }
      """)     
      
      // We can now access the value bar.baz
      val x: Json = src.bar.baz
      
      // And get it as an integer
      val y: Int = x.get[Int]
      
      // Alternatively, we can use an extractor to get the values we want:
      val json""" { "bar": { "baz": $x }, "foo": $z }""" = src
      
      // Now x = 42 and z = "Hello world".
      

      【讨论】:

        【解决方案4】:

        这是你需要的吗? (使用lift-json)

        scala> import net.liftweb.json._
        import net.liftweb.json._
        
        scala> implicit val formats = DefaultFormats
        formats: net.liftweb.json.DefaultFormats.type = net.liftweb.json.DefaultFormats$@79e379
        
        scala> val jsonString = """{"Locations":
          {"list":
            [
              {"description": "some description", "name": "the name", "id": "dev123"},
              {"description": "other description", "name": "other name", "id": "dev59"}
            ]
          }
        }"""
        jsonString: java.lang.String =
        {"Locations":
          {"list":
            [
              {"description": "some description", "name": "the name", "id": "dev123"},
              {"description": "other description", "name": "other name", "id": "dev59"}
            ]
          }
        }
        
        scala> Serialization.read[Map[String, Map[String, List[Map[String, String]]]]](jsonString)
        res43: Map[String,Map[String,List[Map[String,String]]]] = Map(Locations -> Map(list -> List(Map(description -> some desc
        ription, name -> the name, id -> dev123), Map(description -> other description, name -> other name, id -> dev59))))
        
        scala> val json = parse(jsonString)
        json: net.liftweb.json.package.JValue = JObject(List(JField(Locations,JObject(List(JField(list,JArray(List(JObject(List(
        JField(description,JString(some description)), JField(name,JString(the name)), JField(id,JString(dev123)))), JObject(Lis
        t(JField(description,JString(other description)), JField(name,JString(other name)), JField(id,JString(dev59))))))))))))
        
        scala> json \\ "id"
        res44: net.liftweb.json.JsonAST.JValue = JObject(List(JField(id,JString(dev123)), JField(id,JString(dev59))))
        
        scala> compact(render(res44))
        res45: String = {"id":"dev123","id":"dev59"}
        

        【讨论】:

        • 谢谢。虽然看起来比其他答案更复杂。 net.liftweb.json 建议使用第三方库(我猜来自 Lift 框架)。
        • @FilipK:lift-json 作为一个单独的包提供。您不必将整个提升框架引入您的项目。我是新手。当然,必须有一种更简单的 lift-json 方式来做你想做的事。
        【解决方案5】:

        在 JSON 的 SON 分支中,这将起作用。请注意,我没有使用解析器。并不是说它不存在。只是使用构建器方法创建 JSON 对象更容易:

        scala> import nl.typeset.sonofjson._
        import nl.typeset.sonofjson._
        
        scala> var all = obj(
             |   locations = arr(
             |     obj(description = "foo", id = "807",
             |     obj(description = "bar", id = "23324"
             |   )
             | )
        
        scala> all.locations.map(_.id).as[List[String]]
        res2: List[String] = List(23324, 807)
        

        或者使用 for 理解:

        scala> (for (location <- all.locations) yield location.id).as[List[String]]
        res4: List[String] = List(23324, 807)
        

        【讨论】:

          猜你喜欢
          • 2012-05-19
          • 2017-07-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多