【问题标题】:JSON to XML in Scala and dealing with Option() resultScala 中的 JSON 到 XML 并处理 Option() 结果
【发布时间】:2012-02-15 07:50:11
【问题描述】:

考虑 Scala 解释器中的以下内容:

scala> JSON.parseFull("""{"name":"jack","greeting":"hello world"}""")
res6: Option[Any] = Some(Map(name -> jack, greeting -> hello world))

为什么 Map 会在 Some() 中返回?我该如何使用它?

我想把值放在一个 xml 模板中:

<test>
  <name>name goes here</name>
  <greeting>greeting goes here</greeting>
</test>

Scala 从 Some(thing) 中获取地图并在 xml 中获取这些值的方法是什么?

【问题讨论】:

  • 感谢所有出色的答案。关于是否使用模式匹配似乎存在不同的意见。不知道这是否是最纯粹的战争,但我也会用我忠实的“Scala 编程书”来调查它们;-)

标签: scala scala-option


【解决方案1】:

你可能应该使用这样的东西:

res6 collect { case x: Map[String, String] => renderXml(x) }

地点:

def renderXml(m: Map[String, String]) = 
  <test><name>{m.get("name") getOrElse ""}</name></test>

Option[A] 上的collect 方法采用PartialFunction[A, B],是filter(通过谓词)和map(通过函数)的组合。那就是:

opt collect pf
opt filter (a => pf isDefinedAt a) map (a => pf(a))

两者是等价的。当你有一个 optional 值时,你应该使用mapflatMapfiltercollect 等来转换程序中的选项,避免提取选项的内容通过模式匹配或get 方法。你应该永远永远使用Option.get - 这是你做错了的典型标志。应该避免模式匹配,因为它代表您程序中的一个分支,因此会增加圈复杂度 - 您可能希望这样做的唯一时间可能是为了提高性能


其实你有parseJSON方法的结果是Option[Any]的问题(原因是它是Option,大概是解析可能不成功而Option更处理null 的优雅方式比,好吧,null)。

但是我上面的代码的问题是 case x: Map[String, String] 由于类型擦除而无法在运行时检查(即 scala 可以检查该选项是否包含 Map 但不能检查 Map 的类型参数是String。代码会给你一个unchecked警告。

【讨论】:

    【解决方案2】:

    返回 Option 是因为 parseFull 根据输入具有不同的可能返回值,或者它可能根本无法解析输入(给出 None)。因此,除了将键与值相关联的可选 Map 之外,如果 JSON 字符串表示数组,则还可以返回可选的 List

    例子:

    scala> import scala.util.parsing.json.JSON._
    import scala.util.parsing.json.JSON._
    
    scala> parseFull("""{"name":"jack"}""")
    res4: Option[Any] = Some(Map(name -> jack))
    
    scala> parseFull("""[ 100, 200, 300 ]""")
    res6: Option[Any] = Some(List(100.0, 200.0, 300.0))
    

    你可能需要模式匹配来实现你想要的,像这样:

    scala> parseFull("""{"name":"jack","greeting":"hello world"}""") match {
         |   case Some(m) => Console println ("Got a map: " + m)
         |   case _ =>
         | }
    Got a map: Map(name -> jack, greeting -> hello world)
    

    现在,如果要生成 XML 输出,可以使用上面的方法来迭代键/值对:

    import scala.xml.XML
    
    parseFull("""{"name":"jack","greeting":"hello world"}""") match {
      case Some(m: Map[_,_]) =>
        <test>
          {
            m map { case (k,v) =>
              XML.loadString("<%s>%s</%s>".format(k,v,k))
            }
          }
        </test>
    
      case _ =>
    }
    

    【讨论】:

      【解决方案3】:

      parseFull 返回 Option,因为字符串可能不是有效的 JSON(在这种情况下,它将返回 None 而不是 Some)。

      Some 中获取值的常用方法是对其进行模式匹配,如下所示:

      result match {
          case Some(map) =>
              doSomethingWith(map)
          case None =>
              handleTheError()
      }
      

      如果您确定输入将始终有效,因此您不需要处理无效输入的情况,您可以使用Option 上的get 方法,调用时会抛出异常在None

      【讨论】:

      • 应该避免Option上的模式匹配
      • @oxbow_lakes - Option 上的模式匹配通常很笨拙除非您也想匹配内容。
      • +1 我赞同这个答案,并且非常不同意“应该避免”和“笨拙”的说法。模式匹配是解决这个问题最简单、最清晰的方法。
      【解决方案4】:

      你有两个不同的问题。

      1. 输入为Any
      2. 您的数据位于OptionMap 中。

      假设我们有数据:

      val x: Option[Any] = Some(Map("name" -> "jack", "greeting" -> "hi"))
      

      并假设如果有要返回的内容,我们想要返回适当的 XML,否则不返回。然后我们可以使用collect 来收集我们知道如何处理的部分:

      val y = x collect {
        case m: Map[_,_] => m collect {
          case (key: String, value: String) => key -> value
        }
      }
      

      (请注意我们如何将映射中的每个条目分开以确保它将字符串映射到字符串——否则我们将不知道如何进行。我们得到:

      y: Option[scala.collection.immutable.Map[String,String]] =
        Some(Map(name -> jack, greeting -> hi))
      

      好的,这样更好!现在,如果您知道您想要在 XML 中包含哪些字段,您可以询问它们:

      val z = for (m <- y; name <- m.get("name"); greet <- m.get("greeting")) yield {
        <test><name>{name}</name><greeting>{greet}</greeting></test>
      }
      

      在这个(成功的)案例中产生了

      z: Option[scala.xml.Elem] =
        Some(<test><name>jack</name><greeting>hi</greeting></test>)
      

      在不成功的情况下会产生None

      如果您想以&lt;key&gt;value&lt;/key&gt; 的形式包装您在地图中发现的任何内容,则工作量会更大一些,因为 Scala 对标签没有很好的抽象:

      val z = for (m <- y) yield <test>{ m.map { case (tag, text) => xml.Elem(null, tag, xml.Null, xml.TopScope, xml.Text(text)) }}</test>
      

      再次产生

      z: Option[scala.xml.Elem] =
        Some(<test><name>jack</name><greeting>hi</greeting></test>)
      

      (您可以使用get 获取Option 的内容,但如果Option 为空(即None)则会抛出异常。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-14
        • 1970-01-01
        • 2021-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-21
        • 1970-01-01
        相关资源
        最近更新 更多