Created
October 8, 2008 07:53
-
-
Save ymnk/15469 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
not working