Skip to content

Instantly share code, notes, and snippets.

@ymnk
Created October 8, 2008 07:53
Show Gist options
  • Save ymnk/15469 to your computer and use it in GitHub Desktop.
Save ymnk/15469 to your computer and use it in GitHub Desktop.
import scala.xml._
import scala.collection.mutable.{Map, HashMap}
/**
* The XML2JSON object will transform the XML data in scala.xml.Node to
* the string in JSON format.
*/
object XML2JSON {
sealed trait JSONObject
case class JSONObj(objects:List[JSONObject]) extends JSONObject
case class JSONArray(array:List[JSONObject]) extends JSONObject
case class JSONEntry(key:String, value:JSONObject) extends JSONObject
case class JSONString(str:String) extends JSONObject
def apply(xml:Node):String = {
type Env = Map[String, List[JSONObject]]
def emptyMap:Env = HashMap.empty[String, List[JSONObject]]
def env2obj(env:(String, List[JSONObject])) = env match {
case (key, v::Nil) => JSONEntry(key, v)
case (key, v) => JSONEntry(key, JSONArray(v))
}
implicit def opt2list(opt:Option[List[JSONObject]]) = opt match {
case Some(v) => v
case _ => Nil
}
def toJSON(xml:Node, env:Env):Env = {
xml match {
case _:Elem => xml.child match {
case c if c.length==1 && c.first.isInstanceOf[Text] =>
c.first match {
case Text(data) =>
env += xml.label -> (JSONString(data)::env.get(xml.label))
}
case c => {
var foo = c.foldLeft(emptyMap){
(env, c) => toJSON(c, env)
}.map{env2obj}.toList
env += xml.label -> (JSONObj(foo)::env.get(xml.label))
}
}
case Text(data) => { }
case _ => { }
}
env
}
format(JSONObj(toJSON(xml, emptyMap).map{env2obj}.toList))
}
/**
* The following method is not functional for duplicate keys,
* but it will be a good example how to combine with "Option" and "for".
*/
def toJSON(xml:Node):String = {
def _toJSON(xml:Node):Option[JSONObject] = xml match {
case _:Elem => xml.child match {
/**
* The xml.child will be in List if xml is created by XML#load() .
* If xml is created as the literal, it will be in ArrayBuffer
*/
// case Text(data)::Nil =>
// Some(JSONEntry(xml.label, JSONString(data)))
//
case c if c.length==1 && c.first.isInstanceOf[Text] => c.first match {
case Text(data) =>
Some(JSONEntry(xml.label, JSONString(data)))
}
case c => {
val objects=(for(i <- c; j <- _toJSON(i)) yield j).toList
Some(JSONEntry(xml.label, JSONObj(objects)))
}
}
case Text(data) => None
case _ => None
}
format(JSONObj(List(_toJSON(xml).get)))
}
/**
* This method will transform a JSONObject to a string with indentions.
*/
def format(o:JSONObject):String = {
val tab=" "
def wrap(str:String)="\""+str.replaceAll("\"", "\\\"")+"\""
def format(o:JSONObject, indent:Int):String = o match {
case JSONObj(objects) => {
var f=objects.map(format(_, indent+1)).mkString(",\n"+(tab*(indent+1)))
if(f.length>0) f="\n"+(tab*(indent+1))+f+"\n"+(tab*indent)
"{"+f+"}"
}
case JSONArray(array) => {
var f=array.map(format(_, indent+1)).mkString(",\n"+(tab*(indent+1)))
if(f.length>0) f="\n"+(tab*(indent+1))+f+"\n"+(tab*indent)
"["+f+"]"
}
case JSONEntry(key, value) =>
wrap(key)+" : "+format(value, indent)
case JSONString(str) => wrap(str)
}
format(o, 0)
}
}
object XML2JSONTest {
var xml =
<contacts>
<contact>
<name>John Doe</name>
<phone>123-456-7890</phone>
</contact>
<contact>
<name>Jane Doe</name>
<phone>123-456-0000</phone>
</contact>
</contacts>;
def main(arg:Array[String])={
if(arg.length>0){
import java.net.URL
val url=arg(0)
// url="http://twitter.com/statuses/public_timeline.xml"
xml=XML.load(new URL(url).openStream)
}
println(XML2JSON(xml))
}
}
@15hwar
Copy link

15hwar commented Aug 14, 2020

not working

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment