9

For a XML snippet like this:

val fruits =
<fruits>
  <fruit>
    <name>apple</name>
    <taste>ok</taste>
  </fruit>
  <fruit>
    <name>banana</name>
    <taste>better</taste>
  </fruit>
</fruits>

doing something like:

fruits \\ "fruit"

will return a sequence of type scala.xml.NodeSeq with all the fruits and sub nodes inside.

What is the best way to convert this to a list of JSON objects? I'm trying to send my list of fruits back to a browser. I had a look at scala.util.parsing.json.JSONObject and scala.util.parsing.json.JSONArray, but I don't know how to get from NodeSeq to anyone of the latter.

If at all possible, I would love to see how it's done with plain Scala code.

Jack
  • 16,506
  • 19
  • 100
  • 167

3 Answers3

17

This might be relevant. Here is my solution using spray-json:

import scala.xml._
import cc.spray.json._
import cc.spray.json.DefaultJsonProtocol._

implicit object NodeFormat extends JsonFormat[Node] {
  def write(node: Node) =
    if (node.child.count(_.isInstanceOf[Text]) == 1)
      JsString(node.text)
    else
      JsObject(node.child.collect {
        case e: Elem => e.label -> write(e)
      }: _*)

  def read(jsValue: JsValue) = null // not implemented
}

val fruits =
  <fruits>
    <fruit>
      <name>apple</name>
      <taste>
        <sweet>true</sweet>
        <juicy>true</juicy>
      </taste>
    </fruit>
    <fruit>
      <name>banana</name>
      <taste>better</taste>
    </fruit>
  </fruits>

val json = """[{"name":"apple","taste":{"sweet":"true","juicy":"true"}},{"name":"banana","taste":"better"}]"""

assert((fruits \\ "fruit").toSeq.toJson.toString == json)
Community
  • 1
  • 1
elbowich
  • 1,941
  • 1
  • 13
  • 12
  • 1
    This looks great, but your sample does not run. I get the following: error: not found: value JsField. – Jack Mar 05 '12 at 14:59
  • @JacobusR Oops, still using 1.0.1 version of spray-json. There is indeed no `JsField` class in 1.1.0. Replaced it with a tuple. – elbowich Mar 06 '12 at 05:08
  • Works like a charm, thanks. Always lovely to get a runnable bit of code, rather than just a snippet (+1 for that :-). – Jack Mar 06 '12 at 11:58
  • FYI SprayJson has been recently updated, looks like `JsonFormat` has moved – crockpotveggies May 15 '13 at 22:41
  • A `JsonWriter` is more appropriate here as it means you don't need to have a dangling unimplemented `read`. – fommil Aug 21 '13 at 11:16
  • This is pretty cool. The only thing I can think that would be cool to add would be to convert any attributes on the xml nodes to become properties for an object, so rather than `{name:banana}` you'd end up with `{name: banana, attrX:attrVal}` – EdgeCaseBerg Aug 18 '16 at 14:42
  • what about attributes? – nap.gab Oct 02 '17 at 10:02
3

org.json4s makes this pretty simple:

import org.json4s.Xml.toJson

val fruits =
  <fruits>
    <fruit>
      <name>apple</name>
      <taste>
        <sweet>true</sweet>
        <juicy>true</juicy>
      </taste>
    </fruit>
    <fruit>
      <name>banana</name>
      <taste>better</taste>
    </fruit>
  </fruits>

println(toJson(fruits))
Thomas Luechtefeld
  • 1,316
  • 15
  • 23
2

I think you should use ScalaXB to turn the XML into scala classes and then write the appropriate toJson bits to output Json.

Should work a treat.

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
tylerweir
  • 1,265
  • 11
  • 16