[Practicalxml-commits] SF.net SVN: practicalxml:[144] branches/dev-1.1/src
Brought to you by:
kdgregory
|
From: Auto-Generated S. C. M. <pra...@li...> - 2009-09-22 18:05:30
|
Revision: 144
http://practicalxml.svn.sourceforge.net/practicalxml/?rev=144&view=rev
Author: kdgregory
Date: 2009-09-22 17:32:02 +0000 (Tue, 22 Sep 2009)
Log Message:
-----------
xml->json: support arrays
Modified Paths:
--------------
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/package.html
branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java
Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java 2009-09-22 15:27:06 UTC (rev 143)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java 2009-09-22 17:32:02 UTC (rev 144)
@@ -14,9 +14,12 @@
package net.sf.practicalxml.converter.json;
+import java.util.ArrayList;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import net.sf.practicalxml.DomUtil;
@@ -43,16 +46,24 @@
//----------------------------------------------------------------------------
/**
- * Appends the contents of the specified element to an existing buffer.
+ * Converts the subtree rooted at <code>elem</code> to a JSON string.
+ */
+ public String convert(Element elem)
+ {
+ return convert(elem, new StringBuilder(256)).toString();
+ }
+
+
+ /**
+ * Converts the subtree rooted at <code>elem</code> to a JSON string,
+ * appending to an existing buffer. This is useful when building a
+ * JSON assignment statment (eg: "var x = OBJECT").
+ * <p>
* Returns the buffer as a convenience.
*/
public StringBuilder convert(Element elem, StringBuilder buf)
{
- String text = DomUtil.getText(elem);
- if (text != null)
- return appendText(text, buf);
- else
- return appendChildren(elem, buf);
+ return append(buf, elem);
}
@@ -60,8 +71,22 @@
// Internals
//----------------------------------------------------------------------------
- private StringBuilder appendText(String text, StringBuilder buf)
+ // yes, this method is just a restatement of convert(Element,StringBuilder)
+ // I want all the internal appenders named "append".
+ private StringBuilder append(StringBuilder buf, Element elem)
{
+ List<Element> children = DomUtil.getChildren(elem);
+ String text = DomUtil.getText(elem);
+
+ if ((children.size() > 0) || (text == null))
+ return appendObject(buf, children);
+ else
+ return appendText(buf, text);
+ }
+
+
+ private StringBuilder appendText(StringBuilder buf, String text)
+ {
buf.append('"')
.append(JsonUtil.escape(text))
.append('"');
@@ -69,20 +94,74 @@
}
- private StringBuilder appendChildren(Element elem, StringBuilder buf)
+ private StringBuilder appendObject(StringBuilder buf, List<Element> children)
{
+ List<String> names = new ArrayList<String>();
+ Map<String,List<Element>> arrays = new HashMap<String,List<Element>>();
+ Map<String,Element> nonArrays = new HashMap<String,Element>();
+ categorizeChildren(children, names, arrays, nonArrays);
+
buf.append("{");
- List<Element> children = DomUtil.getChildren(elem);
- for (Iterator<Element> childItx = children.iterator() ; childItx.hasNext() ; )
+ for (Iterator<String> itx = names.iterator() ; itx.hasNext() ; )
{
- Element child = childItx.next();
- buf.append(DomUtil.getLocalName(child))
- .append(": ");
- convert(child, buf);
- if (childItx.hasNext())
+ String name = itx.next();
+ buf.append(name).append(": ");
+ if (arrays.containsKey(name))
+ appendArray(buf, arrays.get(name));
+ else
+ append(buf, nonArrays.get(name));
+ if (itx.hasNext())
buf.append(", ");
}
buf.append("}");
return buf;
}
+
+
+ private StringBuilder appendArray(StringBuilder buf, List<Element> values)
+ {
+ buf.append("[");
+ for (Iterator<Element> itx = values.iterator() ; itx.hasNext() ; )
+ {
+ Element child = itx.next();
+ append(buf, child);
+ if (itx.hasNext())
+ buf.append(", ");
+ }
+ buf.append("]");
+ return buf;
+ }
+
+
+ /**
+ * Examines the children of the passed element and categorizes them as
+ * "array" or "not array", while tracking the first appearance of the
+ * element name in document order.
+ */
+ private void categorizeChildren(
+ List<Element> children, List<String> names,
+ Map<String,List<Element>> arrays, Map<String,Element> nonArrays)
+ {
+ for (Element child : children)
+ {
+ String name = DomUtil.getLocalName(child);
+ if (arrays.containsKey(name))
+ {
+ arrays.get(name).add(child);
+ }
+ else if (nonArrays.containsKey(name))
+ {
+ Element prev = nonArrays.remove(name);
+ List<Element> list = new ArrayList<Element>(2);
+ list.add(prev);
+ list.add(child);
+ arrays.put(name, list);
+ }
+ else
+ {
+ nonArrays.put(name, child);
+ names.add(name);
+ }
+ }
+ }
}
Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/package.html
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/package.html 2009-09-22 15:27:06 UTC (rev 143)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/package.html 2009-09-22 17:32:02 UTC (rev 144)
@@ -2,36 +2,39 @@
<body>
This package contains classes to convert JSON (JavaScript Object Notation)
-strings (as defined <a href="http://www.json.org/">json.org</a> ) to and
+strings (as defined <a href="http://www.json.org/">json.org</a> ) to and
from an XML DOM. Although both XML and JSON are textual representations of
hierarchical data, there are some peculiarities in the conversion:
<dl>
<dt> Container Element
<dd> Although elements within a JSON object are named, the object itself is
- not. XML requires a named root element, so the JSON->XML conversion
- creates a root element named "data", and the XML->JSON conversion ignores
- the passed root element.
+ not. XML requires a named root element, so the JSON to XML conversion
+ creates a root element named "data", and the XML to JSON conversion
+ ignores the passed root element.
<dt> Document Order
<dd> In XML, document order is important and may be validated; in JSON, the
order of elements within an object is not important. To support arrays
- (qv), the XML->JSON conversion intentionally breaks document order. And
+ the XML to JSON conversion intentionally breaks document order (qv). And
as JSON has no intrinsic order, you'll need to explicitly re-arrange the
- result of a JSON->XML conversion prior to any validation.
+ result of a JSON to XML conversion prior to any validation.
<dt> Non-String Data
<dd> JSON supports numbers and boolean literals in addition to quote-delimited
- strings. The JSON->XML conversion will handle these values transparently.
- The default XML->JSON conversion writes all content as quote-delimited
+ strings. The JSON to XML conversion will handle these values transparently.
+ The default XML to JSON conversion writes all content as quote-delimited
strings, but there is a conversion option to recognize a limited set of
- non-string elements based on the <code>xsi:type</code> attribute.
-<dt> Arrays
+ non-string elements based on the <code>xsi:type</code> attribute.
+<dt> Arrays, XML to JSON
<dd> XML does not have a defined array construct, but may repeat elements; JSON
has a defined array construct, and repeated elements will overwrite the
- former definition. To avoid this problem, the XML->JSON conversion will
- identify all repeated elements and create an array construct. The default
- JSON->XML conversion creates a parent-children construct (which is more
- palatable to subsequent XML->Java conversion), but a conversion option
- allows conversion to a repeated element.
+ former definition. To avoid this problem, the XML to JSON converter will
+ create a JSON array construct from all siblings with the same localname.
+<dt> Arrays, JSON to XML
+<dd> By default, arrays are converted into a single parent element, with one
+ child named "data" for each element of the array; this is similar to the
+ Java to XML conversion. Optionally, you may convert an array into a series
+ of repeated elements (siblings), all with the same name. When using this
+ option, conversion will fail if presented with a top-level JSON array.
</dl>
</body>
Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java
===================================================================
--- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java 2009-09-22 15:27:06 UTC (rev 143)
+++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java 2009-09-22 17:32:02 UTC (rev 144)
@@ -37,9 +37,8 @@
public void convertAndAssert(String expected, ElementNode rootNode)
{
Element root = rootNode.toDOM().getDocumentElement();
- StringBuilder buf = new Xml2JsonConverter()
- .convert(root, new StringBuilder(256));
- assertEquals(expected, buf.toString());
+ String json = new Xml2JsonConverter().convert(root);
+ assertEquals(expected, json);
}
@@ -86,6 +85,53 @@
}
+ public void testArray() throws Exception
+ {
+ // note that "argle" elements are not adjacent, must become adjacent
+ convertAndAssert(
+ "{foo: \"bar\", argle: [\"bargle\", \"wargle\"], baz: \"bar\"}",
+ element("data",
+ element("foo", text("bar")),
+ element("argle", text("bargle")),
+ element("baz", text("bar")),
+ element("argle", text("wargle"))));
+ }
+
+
+ public void testArrayWithNestedObject() throws Exception
+ {
+ convertAndAssert(
+ "{foo: \"bar\", argle: [\"bargle\", {foo: \"bar\", bar: \"baz\"}]}",
+ element("data",
+ element("foo", text("bar")),
+ element("argle", text("bargle")),
+ element("argle",
+ element("foo", text("bar")),
+ element("bar", text("baz")))));
+ }
+
+
+ // this covers documents parsed with "ignorable whitespace"
+ public void testMixedContentWithWhitespace() throws Exception
+ {
+ convertAndAssert(
+ "{foo: \"bar\"}",
+ element("data",
+ text(" "),
+ element("foo", text("bar")),
+ text("\n")));
+ }
+
+
+ public void testWhitespace() throws Exception
+ {
+ convertAndAssert(
+ "{foo: \" \"}",
+ element("data",
+ element("foo", text(" "))));
+ }
+
+
public void testStringEscaping() throws Exception
{
convertAndAssert(
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|