Scala 中的 JSON 到 XML 并处理 Option() 结果

发布于 2025-01-05 22:27:43 字数 465 浏览 3 评论 0原文

考虑 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>

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

Consider the following from the Scala interpreter:

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

Why is the Map returned in Some() thing? And how do I work with it?

I want to put the values in an xml template:

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

What is the Scala way of getting my map out of Some(thing) and getting those values in the xml?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

你丑哭了我 2025-01-12 22:27:43

您可能应该使用类似这样的内容:

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

其中:

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

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

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

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


实际上,您遇到的问题是 parseJSON 方法是一个 Option[Any] (之所以是一个 Option,大概是解析可能不成功而 Option > 是一种更优雅的处理方式null 比,好吧,null)。

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

You should probably use something like this:

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

Where:

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

The collect method on Option[A] takes a PartialFunction[A, B] and is a combination of filter (by a predicate) and map (by a function). That is:

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

Are both equivalent. When you have an optional value, you should use map, flatMap, filter, collect etc to transform the option in your program, avoiding extracting the option's contents either via a pattern-match or via the get method. You should never, ever use Option.get - it is the canonical sign that you are doing it wrong. Pattern-matching should be avoided because it represents a fork in your program and hence adds to cyclomatic complexity - the only time you might wish to do this might be for performance


Actually you have the issue that the result of the parseJSON method is an Option[Any] (the reason is that it is an Option, presumably, is that the parsing may not succeed and Option is a more graceful way of handling null than, well, null).

But the issue with my code above is that the case x: Map[String, String] cannot be checked at runtime due to type erasure (i.e. scala can check that the option contains a Map but not that the Map's type parameters are both String. The code will get you an unchecked warning.

诗酒趁年少 2025-01-12 22:27:43

返回 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 _ =>
}

An Option is returned because parseFull has different possible return values depending on the input, or it may fail to parse the input at all (giving None). So, aside from an optional Map which associates keys with values, an optional List can be returned as well if the JSON string denoted an array.

Example:

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))

You might need pattern matching in order to achieve what you want, like so:

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)

Now, if you want to generate XML output, you can use the above to iterate over the key/value pairs:

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 _ =>
}
红墙和绿瓦 2025-01-12 22:27:43

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

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

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

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

parseFull returns an Option because the string may not be valid JSON (in which case it will return None instead of Some).

The usual way to get the value out of a Some is to pattern match against it like this:

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

If you're certain the input will always be valid and so you don't need to handle the case of invalid input, you can use the get method on the Option, which will throw an exception when called on None.

笨死的猪 2025-01-12 22:27:43

你有两个不同的问题。

  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>)

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

如果您想以 value 形式包装您在地图中找到的任何内容,则需要更多工作,因为 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 的内容,但如果 >选项为空(即)。)

You have two separate problems.

  1. It's typed as Any.
  2. Your data is inside an Option and a Map.

Let's suppose we have the data:

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

and suppose that we want to return the appropriate XML if there is something to return, but not otherwise. Then we can use collect to gather those parts that we know how to deal with:

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

(note how we've taken each entry in the map apart to make sure it maps a string to a string--we wouldn't know how to proceed otherwise. We get:

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

Okay, that's better! Now if you know which fields you want in your XML, you can ask for them:

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

which in this (successful) case produces

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

and in an unsuccessful case would produce None.

If you instead want to wrap whatever you happen to find in your map in the form <key>value</key>, it's a bit more work because Scala doesn't have a good abstraction for tags:

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

which again produces

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

(You can use get to get the contents of an Option, but it will throw an exception if the Option is empty (i.e. None).)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文