Skip to content

Instantly share code, notes, and snippets.

@Shiti
Last active April 6, 2018 18:11
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Shiti/6ac74ca7f1e9c855675c to your computer and use it in GitHub Desktop.
Save Shiti/6ac74ca7f1e9c855675c to your computer and use it in GitHub Desktop.
Conversion from Play 2.1 JSON to Scala XML and vice versa
import play.api.libs.json._
import xml._
object Utils {
def jsonToXml(jsonData: JsValue): Node = {
val xmlResult = jsonData match {
case JsObject(fields) => {
fields.map {
case (key, value) => {
val result = Elem(null, key, Null, TopScope, false)
result.copy(null, key, Null, TopScope, false, jsonToXml(value).child)
}
}
}
case JsString(content) => Text(content)
case JsBoolean(bool) => Text(bool.toString)
case JsNumber(num) => Text(num.toString())
case JsArray(jsonArray) => jsonArray flatMap {
s => jsonToXml(s)
}
case JsNull => Text("null")
}
<result>{xmlResult}</result>
}
def xmlToJson(xmlData: NodeSeq): JsValue = {
sealed trait XElem
case class XValue(value: String) extends XElem
case class XLeaf(value: (String, XElem), attributes: List[(String, XValue)]) extends XElem
case class XNode(fields: List[(String, XElem)]) extends XElem
case class XArray(elems: List[XElem]) extends XElem
def empty_?(node: Node) = node.child.isEmpty
def leaf_?(node: Node) = !node.descendant.find(_.isInstanceOf[Elem]).isDefined
def array_?(nodeNames: Seq[String]) = nodeNames.size != 1 && nodeNames.toList.distinct.size == 1
def directChildren(n: Node): NodeSeq = n.child.filter(c => c.isInstanceOf[Elem])
def nameOf(n: Node) = (if (Option(n.prefix).nonEmpty) n.prefix + ":" else "") + n.label
def buildAttributes(n: Node) = n.attributes.map((a: MetaData) => (a.key, XValue(a.value.text))).toList
def mkFields(xs: List[(String, XElem)]) =
xs.flatMap {
case (name, value) => (value, toJValue(value)) match {
case (XLeaf(v, x :: xs), o: JsObject) => o :: Nil
case (_, json) => Json.obj(name -> json) :: Nil
}
}
def toJValue(x: XElem): JsValue = x match {
case XValue(s) => Json.toJson(s)
case XLeaf((name, value), attributes) => (value, attributes) match {
case (_, Nil) => toJValue(value)
case (XValue(""), xs) => Json.toJson(mkFields(xs))
case (_, xs) => Json.obj(Json.obj(name -> toJValue(value)).toString() -> Json.toJson(mkFields(xs)))
}
case XNode(xs) => {
val result = mkFields(xs).reduce(_ ++ _)
Json.toJson(result)
}
case XArray(elems) => Json.toJson(elems.map(toJValue))
}
def buildNodes(xml: NodeSeq): List[XElem] = xml match {
case n: Node =>
if (empty_?(n)) XLeaf((nameOf(n), XValue("")), buildAttributes(n)) :: Nil
else if (leaf_?(n)) XLeaf((nameOf(n), XValue(n.text)), buildAttributes(n)) :: Nil
else {
val children = directChildren(n)
XNode(buildAttributes(n) ++ children.map(nameOf).toList.zip(buildNodes(children))) :: Nil
}
case nodes: NodeSeq =>
val allLabels = nodes.map(_.label)
if (array_?(allLabels)) {
val arr = XArray(nodes.toList.flatMap {
n =>
if (leaf_?(n) && n.attributes.length == 0) XValue(n.text) :: Nil
else buildNodes(n)
})
XLeaf((allLabels(0), arr), Nil) :: Nil
} else nodes.toList.flatMap(buildNodes)
}
buildNodes(xmlData) match {
case List(x@XLeaf(_, _ :: _)) => toJValue(x)
case List(x) => Json.obj(nameOf(xmlData.head) -> toJValue(x))
case x => Json.toJson(x.map(toJValue))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment