add xml to json library
This commit is contained in:
@@ -74,6 +74,33 @@
|
|||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>fastjson</artifactId>
|
<artifactId>fastjson</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.kxml</groupId>
|
||||||
|
<artifactId>kxml2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.json</groupId>
|
||||||
|
<artifactId>json</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.junit.vintage</groupId>
|
||||||
|
<artifactId>junit-vintage-engine</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.skyscreamer</groupId>
|
||||||
|
<artifactId>jsonassert</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
69
fizz-common/src/main/java/we/xml/FileReader.java
Normal file
69
fizz-common/src/main/java/we/xml/FileReader.java
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Arnaud Guyon
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package we.xml;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by arnaud on 03/12/2016.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class FileReader {
|
||||||
|
|
||||||
|
public static String readFileFromAsset(String fileName) {
|
||||||
|
try {
|
||||||
|
InputStream inputStream = new FileInputStream(fileName);
|
||||||
|
String result = readFileFromInputStream(inputStream);
|
||||||
|
inputStream.close();
|
||||||
|
return result;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace(); // TODO
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readFileFromInputStream(InputStream inputStream) {
|
||||||
|
|
||||||
|
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
try {
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
result.append(line);
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
} catch (IOException exception) {
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
bufferedReader.close();
|
||||||
|
} catch (IOException e2) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
inputStreamReader.close();
|
||||||
|
} catch (IOException e2) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
311
fizz-common/src/main/java/we/xml/JsonToXml.java
Normal file
311
fizz-common/src/main/java/we/xml/JsonToXml.java
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Arnaud Guyon
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package we.xml;
|
||||||
|
|
||||||
|
//import android.util.Xml;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.kxml2.io.KXmlSerializer;
|
||||||
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.DecimalFormatSymbols;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.xml.transform.OutputKeys;
|
||||||
|
import javax.xml.transform.Source;
|
||||||
|
import javax.xml.transform.Transformer;
|
||||||
|
import javax.xml.transform.TransformerFactory;
|
||||||
|
import javax.xml.transform.stream.StreamResult;
|
||||||
|
import javax.xml.transform.stream.StreamSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts JSON to XML <br/>
|
||||||
|
* <br/>
|
||||||
|
* Add default tag prefix(-) by Francis Dong<br/>
|
||||||
|
* Change default content name to #text by Francis Dong<br/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class JsonToXml {
|
||||||
|
|
||||||
|
private static final String DEFAULT_TAG_PREFIX = "-";
|
||||||
|
private static final int DEFAULT_INDENTATION = 3;
|
||||||
|
// TODO: Set up Locale in the builder
|
||||||
|
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private JSONObject mJson;
|
||||||
|
private HashSet<String> mForcedAttributes = new HashSet<>();
|
||||||
|
private HashSet<String> mForcedContent = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param jsonObject a JSON object
|
||||||
|
*/
|
||||||
|
public Builder(JSONObject jsonObject) {
|
||||||
|
mJson = jsonObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param inputStream InputStream containing the JSON
|
||||||
|
*/
|
||||||
|
public Builder(InputStream inputStream) {
|
||||||
|
this(FileReader.readFileFromInputStream(inputStream));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param jsonString String containing the JSON
|
||||||
|
*/
|
||||||
|
public Builder(String jsonString) {
|
||||||
|
try {
|
||||||
|
mJson = new JSONObject(jsonString);
|
||||||
|
} catch (JSONException exception) {
|
||||||
|
exception.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a TAG to be an attribute of the parent TAG
|
||||||
|
* @param path Path for the attribute, using format like "/parentTag/childTag/childTagAttribute"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder forceAttribute(String path) {
|
||||||
|
mForcedAttributes.add(path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a TAG to be the content of its parent TAG
|
||||||
|
* @param path Path for the content, using format like "/parentTag/contentTag"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder forceContent(String path) {
|
||||||
|
mForcedContent.add(path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the JsonToXml object
|
||||||
|
* @return a JsonToXml instance
|
||||||
|
*/
|
||||||
|
public JsonToXml build() {
|
||||||
|
return new JsonToXml(mJson, mForcedAttributes, mForcedContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject mJson;
|
||||||
|
private HashSet<String> mForcedAttributes;
|
||||||
|
private HashSet<String> mForcedContent;
|
||||||
|
|
||||||
|
private JsonToXml(JSONObject jsonObject, HashSet<String> forcedAttributes, HashSet<String> forcedContent) {
|
||||||
|
mJson = jsonObject;
|
||||||
|
mForcedAttributes = forcedAttributes;
|
||||||
|
mForcedContent = forcedContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the XML
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
Node rootNode = new Node(null, "");
|
||||||
|
prepareObject(rootNode, mJson);
|
||||||
|
return nodeToXML(rootNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the formatted XML with a default indent (3 spaces)
|
||||||
|
*/
|
||||||
|
public String toFormattedString() {
|
||||||
|
return toFormattedString(DEFAULT_INDENTATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param indent size of the indent (number of spaces)
|
||||||
|
* @return the formatted XML
|
||||||
|
*/
|
||||||
|
public String toFormattedString(int indent) {
|
||||||
|
String input = toString();
|
||||||
|
try {
|
||||||
|
Source xmlInput = new StreamSource(new StringReader(input));
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
StreamResult xmlOutput = new StreamResult(stringWriter);
|
||||||
|
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||||
|
Transformer transformer = transformerFactory.newTransformer();
|
||||||
|
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||||
|
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "" + indent);
|
||||||
|
transformer.transform(xmlInput, xmlOutput);
|
||||||
|
return xmlOutput.getWriter().toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e); // TODO: do my own
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String nodeToXML(Node node) {
|
||||||
|
// XmlSerializer serializer = Xml.newSerializer();
|
||||||
|
try {
|
||||||
|
XmlSerializer serializer = new KXmlSerializer();
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
serializer.setOutput(writer);
|
||||||
|
serializer.startDocument("UTF-8", true);
|
||||||
|
|
||||||
|
nodeToXml(serializer, node);
|
||||||
|
|
||||||
|
serializer.endDocument();
|
||||||
|
return writer.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e); // TODO: do my own
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void nodeToXml(XmlSerializer serializer, Node node) throws IOException {
|
||||||
|
String nodeName = node.getName();
|
||||||
|
if (nodeName != null) {
|
||||||
|
serializer.startTag("", nodeName);
|
||||||
|
|
||||||
|
for (Node.Attribute attribute : node.getAttributes()) {
|
||||||
|
serializer.attribute("", attribute.mKey, attribute.mValue);
|
||||||
|
}
|
||||||
|
String nodeContent = node.getContent();
|
||||||
|
if (nodeContent != null) {
|
||||||
|
serializer.text(nodeContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Node subNode : node.getChildren()) {
|
||||||
|
nodeToXml(serializer, subNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeName != null) {
|
||||||
|
serializer.endTag("", nodeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareObject(Node node, JSONObject json) {
|
||||||
|
Iterator<String> keyterator = json.keys();
|
||||||
|
while (keyterator.hasNext()) {
|
||||||
|
String key = keyterator.next();
|
||||||
|
Object object = json.opt(key);
|
||||||
|
if (object != null) {
|
||||||
|
if (object instanceof JSONObject) {
|
||||||
|
JSONObject subObject = (JSONObject) object;
|
||||||
|
String path = node.getPath() + "/" + key;
|
||||||
|
Node subNode = new Node(key, path);
|
||||||
|
node.addChild(subNode);
|
||||||
|
prepareObject(subNode, subObject);
|
||||||
|
} else if (object instanceof JSONArray) {
|
||||||
|
JSONArray array = (JSONArray) object;
|
||||||
|
prepareArray(node, key, array);
|
||||||
|
} else {
|
||||||
|
String path = node.getPath() + "/" + key;
|
||||||
|
// JSON numbers are represented either Integer or Double (IEEE 754)
|
||||||
|
// Long may be represented in scientific notation because they are stored as Double
|
||||||
|
// This workaround attempts to represent Long and Double objects accordingly
|
||||||
|
String value;
|
||||||
|
if (object instanceof Double) {
|
||||||
|
double d = (double) object;
|
||||||
|
// If it is a Long
|
||||||
|
if (d % 1 == 0) {
|
||||||
|
value = Long.toString((long) d);
|
||||||
|
} else {
|
||||||
|
// TODO: Set up number of decimal digits per attribute in the builder
|
||||||
|
// Set only once. Represent all double numbers up to 20 decimal digits
|
||||||
|
if (DECIMAL_FORMAT.getMaximumFractionDigits() == 0) {
|
||||||
|
DECIMAL_FORMAT.setMaximumFractionDigits(20);
|
||||||
|
}
|
||||||
|
value = DECIMAL_FORMAT.format(d);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Integer, Boolean and String are handled here
|
||||||
|
value = object.toString();
|
||||||
|
}
|
||||||
|
if (isAttribute(path)) {
|
||||||
|
if(key.startsWith(DEFAULT_TAG_PREFIX)) {
|
||||||
|
key = key.substring(1, key.length());
|
||||||
|
}
|
||||||
|
node.addAttribute(key, value);
|
||||||
|
} else if (isContent(path) ) {
|
||||||
|
node.setContent(value);
|
||||||
|
} else {
|
||||||
|
Node subNode = new Node(key, node.getPath());
|
||||||
|
subNode.setContent(value);
|
||||||
|
node.addChild(subNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareArray(Node node, String key, JSONArray array) {
|
||||||
|
int count = array.length();
|
||||||
|
String path = node.getPath() + "/" + key;
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
Node subNode = new Node(key, path);
|
||||||
|
Object object = array.opt(i);
|
||||||
|
if (object != null) {
|
||||||
|
if (object instanceof JSONObject) {
|
||||||
|
JSONObject jsonObject = (JSONObject) object;
|
||||||
|
prepareObject(subNode, jsonObject);
|
||||||
|
} else if (object instanceof JSONArray) {
|
||||||
|
JSONArray subArray = (JSONArray) object;
|
||||||
|
prepareArray(subNode, key, subArray);
|
||||||
|
} else {
|
||||||
|
String value = object.toString();
|
||||||
|
subNode.setName(key);
|
||||||
|
subNode.setContent(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.addChild(subNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAttribute(String path) {
|
||||||
|
if (mForcedAttributes.contains(path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String[] paths = path.split("/");
|
||||||
|
if (paths[paths.length - 1].startsWith(DEFAULT_TAG_PREFIX)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isContent(String path) {
|
||||||
|
if (mForcedContent.contains(path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String[] paths = path.split("/");
|
||||||
|
if ("#text".equals(paths[paths.length - 1])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
81
fizz-common/src/main/java/we/xml/Node.java
Normal file
81
fizz-common/src/main/java/we/xml/Node.java
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Arnaud Guyon
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package we.xml;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to store data when converting from JSON to XML
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* package */ class Node {
|
||||||
|
|
||||||
|
/* package */ class Attribute {
|
||||||
|
String mKey;
|
||||||
|
String mValue;
|
||||||
|
Attribute(String key, String value) {
|
||||||
|
mKey = key;
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mName;
|
||||||
|
private String mPath;
|
||||||
|
private String mContent;
|
||||||
|
private ArrayList<Attribute> mAttributes = new ArrayList<>();
|
||||||
|
private ArrayList<Node> mChildren = new ArrayList<>();
|
||||||
|
|
||||||
|
/* package */ Node(String name, String path) {
|
||||||
|
mName = name;
|
||||||
|
mPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ void addAttribute(String key, String value) {
|
||||||
|
mAttributes.add(new Attribute(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ void setContent(String content) {
|
||||||
|
mContent = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ void setName(String name) {
|
||||||
|
mName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ void addChild(Node child) {
|
||||||
|
mChildren.add(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ ArrayList<Attribute> getAttributes() {
|
||||||
|
return mAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ String getContent() {
|
||||||
|
return mContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ ArrayList<Node> getChildren() {
|
||||||
|
return mChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ String getPath() {
|
||||||
|
return mPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ String getName() {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
}
|
||||||
108
fizz-common/src/main/java/we/xml/Tag.java
Normal file
108
fizz-common/src/main/java/we/xml/Tag.java
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package we.xml;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2016 Arnaud Guyon
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to store XML hierarchy
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Tag {
|
||||||
|
|
||||||
|
private String mPath;
|
||||||
|
private String mName;
|
||||||
|
private ArrayList<Tag> mChildren = new ArrayList<>();
|
||||||
|
private String mContent;
|
||||||
|
|
||||||
|
/* package */ Tag(String path, String name) {
|
||||||
|
mPath = path;
|
||||||
|
mName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ void addChild(Tag tag) {
|
||||||
|
mChildren.add(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ void setContent(String content) {
|
||||||
|
// checks that there is a relevant content (not only spaces or \n)
|
||||||
|
boolean hasContent = false;
|
||||||
|
if (content != null) {
|
||||||
|
for(int i=0; i<content.length(); ++i) {
|
||||||
|
char c = content.charAt(i);
|
||||||
|
if ((c != ' ') && (c != '\n')) {
|
||||||
|
hasContent = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasContent) {
|
||||||
|
mContent = content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ String getName() {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ String getContent() {
|
||||||
|
return mContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ ArrayList<Tag> getChildren() {
|
||||||
|
return mChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ boolean hasChildren() {
|
||||||
|
return (mChildren.size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ int getChildrenCount() {
|
||||||
|
return mChildren.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ Tag getChild(int index) {
|
||||||
|
if ((index >= 0) && (index < mChildren.size())) {
|
||||||
|
return mChildren.get(index);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ HashMap<String, ArrayList<Tag>> getGroupedElements() {
|
||||||
|
HashMap<String, ArrayList<Tag>> groups = new HashMap<>();
|
||||||
|
for(Tag child : mChildren) {
|
||||||
|
String key = child.getName();
|
||||||
|
ArrayList<Tag> group = groups.get(key);
|
||||||
|
if (group == null) {
|
||||||
|
group = new ArrayList<>();
|
||||||
|
groups.put(key, group);
|
||||||
|
}
|
||||||
|
group.add(child);
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ String getPath() {
|
||||||
|
return mPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Tag: " + mName + ", " + mChildren.size() + " children, Content: " + mContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
675
fizz-common/src/main/java/we/xml/XmlToJson.java
Normal file
675
fizz-common/src/main/java/we/xml/XmlToJson.java
Normal file
@@ -0,0 +1,675 @@
|
|||||||
|
package we.xml;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2016 Arnaud Guyon
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
import org.xmlpull.v1.XmlPullParserFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static java.util.regex.Pattern.DOTALL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts XML to JSON<br/>
|
||||||
|
* <br/>
|
||||||
|
* Add default tag prefix(-) by Francis Dong<br/>
|
||||||
|
* Change default content name to #text by Francis Dong<br/>
|
||||||
|
* Add handerListItem method to handler list item by Francis Dong<br/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class XmlToJson {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(XmlToJson.class);
|
||||||
|
|
||||||
|
private static final String TAG = "XmlToJson";
|
||||||
|
// private static final String DEFAULT_CONTENT_NAME = "content";
|
||||||
|
private static final String DEFAULT_CONTENT_NAME = "#text";
|
||||||
|
private static final String DEFAULT_TAG_PREFIX = "-";
|
||||||
|
private static final String DEFAULT_ENCODING = "utf-8";
|
||||||
|
private static final String DEFAULT_INDENTATION = " ";
|
||||||
|
private String mIndentationPattern = DEFAULT_INDENTATION;
|
||||||
|
|
||||||
|
// default values when a Tag is empty
|
||||||
|
private static final String DEFAULT_EMPTY_STRING = "";
|
||||||
|
private static final int DEFAULT_EMPTY_INTEGER = 0;
|
||||||
|
private static final long DEFAULT_EMPTY_LONG = 0;
|
||||||
|
private static final double DEFAULT_EMPTY_DOUBLE = 0;
|
||||||
|
private static final boolean DEFAULT_EMPTY_BOOLEAN = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder class to create a XmlToJson object
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private StringReader mStringSource;
|
||||||
|
private InputStream mInputStreamSource;
|
||||||
|
private String mInputEncoding = DEFAULT_ENCODING;
|
||||||
|
private HashSet<String> mForceListPaths = new HashSet<>();
|
||||||
|
private HashSet<Pattern> mForceListPatterns = new HashSet<>();
|
||||||
|
private HashMap<String, String> mAttributeNameReplacements = new HashMap<>();
|
||||||
|
private HashMap<String, String> mContentNameReplacements = new HashMap<>();
|
||||||
|
private HashMap<String, Class> mForceClassForPath = new HashMap<>(); // Integer, Long, Double, Boolean
|
||||||
|
private HashSet<String> mSkippedAttributes = new HashSet<>();
|
||||||
|
private HashSet<String> mSkippedTags = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param xmlSource XML source
|
||||||
|
*/
|
||||||
|
public Builder(String xmlSource) {
|
||||||
|
mStringSource = new StringReader(xmlSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param inputStreamSource XML source
|
||||||
|
* @param inputEncoding XML encoding format, can be null (uses UTF-8 if null).
|
||||||
|
*/
|
||||||
|
public Builder(InputStream inputStreamSource, String inputEncoding) {
|
||||||
|
mInputStreamSource = inputStreamSource;
|
||||||
|
mInputEncoding = (inputEncoding != null) ? inputEncoding : DEFAULT_ENCODING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a XML Tag to be interpreted as a list
|
||||||
|
*
|
||||||
|
* @param path Path for the tag, with format like "/parentTag/childTag/tagAsAList"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder forceList(String path) {
|
||||||
|
mForceListPaths.add(path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a XML Tag to be interpreted as a list, using a RegEx pattern for the path
|
||||||
|
*
|
||||||
|
* @param pattern Path for the tag using RegEx, like "*childTag/tagAsAList"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder forceListPattern(String pattern) {
|
||||||
|
Pattern pat = Pattern.compile(pattern, DOTALL);
|
||||||
|
mForceListPatterns.add(pat);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the name of an attribute
|
||||||
|
*
|
||||||
|
* @param attributePath Path for the attribute, using format like "/parentTag/childTag/childTagAttribute"
|
||||||
|
* @param replacementName Name used for replacement (childTagAttribute becomes replacementName)
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder setAttributeName(String attributePath, String replacementName) {
|
||||||
|
mAttributeNameReplacements.put(attributePath, replacementName);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the name of the key for a XML content
|
||||||
|
* In XML there is no extra key name for a tag content. So a default name "content" is used.
|
||||||
|
* This "content" name can be replaced with a custom name.
|
||||||
|
*
|
||||||
|
* @param contentPath Path for the Tag that holds the content, using format like "/parentTag/childTag"
|
||||||
|
* @param replacementName Name used in place of the default "content" key
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder setContentName(String contentPath, String replacementName) {
|
||||||
|
mContentNameReplacements.put(contentPath, replacementName);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force an attribute or content value to be a INTEGER. A default value is used if the content is missing.
|
||||||
|
* @param path Path for the Tag content or Attribute, using format like "/parentTag/childTag"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder forceIntegerForPath(String path) {
|
||||||
|
mForceClassForPath.put(path, Integer.class);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force an attribute or content value to be a LONG. A default value is used if the content is missing.
|
||||||
|
* @param path Path for the Tag content or Attribute, using format like "/parentTag/childTag"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder forceLongForPath(String path) {
|
||||||
|
mForceClassForPath.put(path, Long.class);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force an attribute or content value to be a DOUBLE. A default value is used if the content is missing.
|
||||||
|
* @param path Path for the Tag content or Attribute, using format like "/parentTag/childTag"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder forceDoubleForPath(String path) {
|
||||||
|
mForceClassForPath.put(path, Double.class);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force an attribute or content value to be a BOOLEAN. A default value is used if the content is missing.
|
||||||
|
* @param path Path for the Tag content or Attribute, using format like "/parentTag/childTag"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder forceBooleanForPath(String path) {
|
||||||
|
mForceClassForPath.put(path, Boolean.class);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skips a Tag (will not be present in the JSON)
|
||||||
|
*
|
||||||
|
* @param path Path for the Tag, using format like "/parentTag/childTag"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder skipTag(String path) {
|
||||||
|
mSkippedTags.add(path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skips an attribute (will not be present in the JSON)
|
||||||
|
*
|
||||||
|
* @param path Path for the Attribute, using format like "/parentTag/childTag/ChildTagAttribute"
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public Builder skipAttribute(String path) {
|
||||||
|
mSkippedAttributes.add(path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the XmlToJson object
|
||||||
|
*
|
||||||
|
* @return a XmlToJson instance
|
||||||
|
*/
|
||||||
|
public XmlToJson build() {
|
||||||
|
return new XmlToJson(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringReader mStringSource;
|
||||||
|
private InputStream mInputStreamSource;
|
||||||
|
private String mInputEncoding;
|
||||||
|
private HashSet<String> mForceListPaths;
|
||||||
|
private HashSet<Pattern> mForceListPatterns = new HashSet<>();
|
||||||
|
private HashMap<String, String> mAttributeNameReplacements;
|
||||||
|
private HashMap<String, String> mContentNameReplacements;
|
||||||
|
private HashMap<String, Class> mForceClassForPath;
|
||||||
|
private HashSet<String> mSkippedAttributes = new HashSet<>();
|
||||||
|
private HashSet<String> mSkippedTags = new HashSet<>();
|
||||||
|
private JSONObject mJsonObject; // Used for caching the result
|
||||||
|
|
||||||
|
private XmlToJson(Builder builder) {
|
||||||
|
mStringSource = builder.mStringSource;
|
||||||
|
mInputStreamSource = builder.mInputStreamSource;
|
||||||
|
mInputEncoding = builder.mInputEncoding;
|
||||||
|
mForceListPaths = builder.mForceListPaths;
|
||||||
|
mForceListPatterns = builder.mForceListPatterns;
|
||||||
|
mAttributeNameReplacements = builder.mAttributeNameReplacements;
|
||||||
|
mContentNameReplacements = builder.mContentNameReplacements;
|
||||||
|
mForceClassForPath = builder.mForceClassForPath;
|
||||||
|
mSkippedAttributes = builder.mSkippedAttributes;
|
||||||
|
mSkippedTags = builder.mSkippedTags;
|
||||||
|
|
||||||
|
mJsonObject = convertToJSONObject(); // Build now so that the InputStream can be closed just after
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the JSONObject built from the XML
|
||||||
|
*/
|
||||||
|
public JSONObject toJson() {
|
||||||
|
return mJsonObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject convertToJSONObject() {
|
||||||
|
try {
|
||||||
|
Tag parentTag = new Tag("", "xml");
|
||||||
|
|
||||||
|
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||||
|
factory.setNamespaceAware(false); // tags with namespace are taken as-is ("namespace:tagname")
|
||||||
|
XmlPullParser xpp = factory.newPullParser();
|
||||||
|
|
||||||
|
setInput(xpp);
|
||||||
|
|
||||||
|
int eventType = xpp.getEventType();
|
||||||
|
while (eventType != XmlPullParser.START_DOCUMENT) {
|
||||||
|
eventType = xpp.next();
|
||||||
|
}
|
||||||
|
readTags(parentTag, xpp);
|
||||||
|
|
||||||
|
unsetInput();
|
||||||
|
|
||||||
|
return convertTagToJson(parentTag, false);
|
||||||
|
} catch (XmlPullParserException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInput(XmlPullParser xpp) {
|
||||||
|
if (mStringSource != null) {
|
||||||
|
try {
|
||||||
|
xpp.setInput(mStringSource);
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
xpp.setInput(mInputStreamSource, mInputEncoding);
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unsetInput() {
|
||||||
|
if (mStringSource != null) {
|
||||||
|
mStringSource.close();
|
||||||
|
}
|
||||||
|
// else the InputStream has been given by the user, it is not our role to close it
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readTags(Tag parent, XmlPullParser xpp) {
|
||||||
|
try {
|
||||||
|
int eventType;
|
||||||
|
do {
|
||||||
|
eventType = xpp.next();
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
String tagName = xpp.getName();
|
||||||
|
String path = parent.getPath() + "/" + tagName;
|
||||||
|
|
||||||
|
boolean skipTag = mSkippedTags.contains(path);
|
||||||
|
|
||||||
|
Tag child = new Tag(path, tagName);
|
||||||
|
if (!skipTag) {
|
||||||
|
parent.addChild(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes are taken into account as key/values in the child
|
||||||
|
int attrCount = xpp.getAttributeCount();
|
||||||
|
for (int i = 0; i < attrCount; ++i) {
|
||||||
|
String attrName = xpp.getAttributeName(i);
|
||||||
|
String attrValue = xpp.getAttributeValue(i);
|
||||||
|
String attrPath = parent.getPath() + "/" + child.getName() + "/" + attrName;
|
||||||
|
|
||||||
|
// Skip Attributes
|
||||||
|
if (mSkippedAttributes.contains(attrPath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
attrName = getAttributeNameReplacement(attrPath, attrName);
|
||||||
|
Tag attribute = new Tag(attrPath, attrName);
|
||||||
|
attribute.setContent(attrValue);
|
||||||
|
child.addChild(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
readTags(child, xpp);
|
||||||
|
} else if (eventType == XmlPullParser.TEXT) {
|
||||||
|
String text = xpp.getText();
|
||||||
|
parent.setContent(text);
|
||||||
|
} else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
return;
|
||||||
|
} else if (eventType == XmlPullParser.END_DOCUMENT) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
LOGGER.info("{} unknown xml eventType {}", TAG, eventType);
|
||||||
|
}
|
||||||
|
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||||
|
} catch (XmlPullParserException | IOException | NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject convertTagToJson(Tag tag, boolean isListElement) {
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
|
||||||
|
// Content is injected as a key/value
|
||||||
|
if (tag.getContent() != null) {
|
||||||
|
String path = tag.getPath();
|
||||||
|
String name = getContentNameReplacement(path, DEFAULT_CONTENT_NAME);
|
||||||
|
putContent(path, json, name, tag.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
HashMap<String, ArrayList<Tag>> groups = tag.getGroupedElements(); // groups by tag names so that we can detect lists or single elements
|
||||||
|
for (ArrayList<Tag> group : groups.values()) {
|
||||||
|
|
||||||
|
if (group.size() == 1) { // element, or list of 1
|
||||||
|
Tag child = group.get(0);
|
||||||
|
if (isForcedList(child)) { // list of 1
|
||||||
|
JSONArray list = new JSONArray();
|
||||||
|
list.put(handerListItem(child.getPath(), convertTagToJson(child, true)));
|
||||||
|
String childrenNames = child.getName();
|
||||||
|
json.put(childrenNames, list);
|
||||||
|
} else { // stand alone element
|
||||||
|
if (child.hasChildren()) {
|
||||||
|
JSONObject jsonChild = convertTagToJson(child, false);
|
||||||
|
json.put(child.getName(), jsonChild);
|
||||||
|
} else {
|
||||||
|
String path = child.getPath();
|
||||||
|
putContent(path, json, child.getName(), child.getContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // list
|
||||||
|
JSONArray list = new JSONArray();
|
||||||
|
for (Tag child : group) {
|
||||||
|
list.put(handerListItem(child.getPath(), convertTagToJson(child, true)));
|
||||||
|
}
|
||||||
|
String childrenNames = group.get(0).getName();
|
||||||
|
json.put(childrenNames, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to single value if JSONObject only contain content key <br/>
|
||||||
|
* Returns "abc" if JSONObject is {"#text": "abc"} <br/>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "rawtypes", "unused" })
|
||||||
|
private Object handerListItem(String path, JSONObject json) {
|
||||||
|
if(json.length() == 1 && json.has(DEFAULT_CONTENT_NAME)) {
|
||||||
|
Object val = json.get(DEFAULT_CONTENT_NAME);
|
||||||
|
if(val == null) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
String content = String.valueOf(val);
|
||||||
|
try {
|
||||||
|
// checks if the user wants to force a class (Int, Double... for a given path)
|
||||||
|
Class forcedClass = mForceClassForPath.get(path);
|
||||||
|
if (forcedClass == null) { // default behaviour, put it as a String
|
||||||
|
return content;
|
||||||
|
} else {
|
||||||
|
if (forcedClass == Integer.class) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(content);
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
return DEFAULT_EMPTY_INTEGER;
|
||||||
|
}
|
||||||
|
} else if (forcedClass == Long.class) {
|
||||||
|
try {
|
||||||
|
return Long.parseLong(content);
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
return DEFAULT_EMPTY_LONG;
|
||||||
|
}
|
||||||
|
} else if (forcedClass == Double.class) {
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(content);
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
return DEFAULT_EMPTY_DOUBLE;
|
||||||
|
}
|
||||||
|
} else if (forcedClass == Boolean.class) {
|
||||||
|
if (content == null) {
|
||||||
|
return DEFAULT_EMPTY_BOOLEAN;
|
||||||
|
} else if (content.equalsIgnoreCase("true")) {
|
||||||
|
return true;
|
||||||
|
} else if (content.equalsIgnoreCase("false")) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return DEFAULT_EMPTY_BOOLEAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (JSONException exception) {
|
||||||
|
// keep continue in case of error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putContent(String path, JSONObject json, String tag, String content) {
|
||||||
|
try {
|
||||||
|
// checks if the user wants to force a class (Int, Double... for a given path)
|
||||||
|
Class forcedClass = mForceClassForPath.get(path);
|
||||||
|
if (forcedClass == null) { // default behaviour, put it as a String
|
||||||
|
if (content == null) {
|
||||||
|
content = DEFAULT_EMPTY_STRING;
|
||||||
|
}
|
||||||
|
json.put(tag, content);
|
||||||
|
} else {
|
||||||
|
if (forcedClass == Integer.class) {
|
||||||
|
try {
|
||||||
|
Integer number = Integer.parseInt(content);
|
||||||
|
json.put(tag, number);
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
json.put(tag, DEFAULT_EMPTY_INTEGER);
|
||||||
|
}
|
||||||
|
} else if (forcedClass == Long.class) {
|
||||||
|
try {
|
||||||
|
Long number = Long.parseLong(content);
|
||||||
|
json.put(tag, number);
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
json.put(tag, DEFAULT_EMPTY_LONG);
|
||||||
|
}
|
||||||
|
} else if (forcedClass == Double.class) {
|
||||||
|
try {
|
||||||
|
Double number = Double.parseDouble(content);
|
||||||
|
json.put(tag, number);
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
json.put(tag, DEFAULT_EMPTY_DOUBLE);
|
||||||
|
}
|
||||||
|
} else if (forcedClass == Boolean.class) {
|
||||||
|
if (content == null) {
|
||||||
|
json.put(tag, DEFAULT_EMPTY_BOOLEAN);
|
||||||
|
} else if (content.equalsIgnoreCase("true")) {
|
||||||
|
json.put(tag, true);
|
||||||
|
} else if (content.equalsIgnoreCase("false")) {
|
||||||
|
json.put(tag, false);
|
||||||
|
} else {
|
||||||
|
json.put(tag, DEFAULT_EMPTY_BOOLEAN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (JSONException exception) {
|
||||||
|
// keep continue in case of error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isForcedList(Tag tag) {
|
||||||
|
String path = tag.getPath();
|
||||||
|
if (mForceListPaths.contains(path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for(Pattern pattern : mForceListPatterns) {
|
||||||
|
Matcher matcher = pattern.matcher(path);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAttributeNameReplacement(String path, String defaultValue) {
|
||||||
|
String result = mAttributeNameReplacements.get(path);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return DEFAULT_TAG_PREFIX + defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getContentNameReplacement(String path, String defaultValue) {
|
||||||
|
String result = mContentNameReplacements.get(path);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (mJsonObject != null) {
|
||||||
|
return mJsonObject.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the Json with indentation and line breaks
|
||||||
|
*
|
||||||
|
* @param indentationPattern indentation to use, for example " " or "\t".
|
||||||
|
* if null, use the default 3 spaces indentation
|
||||||
|
* @return the formatted Json
|
||||||
|
*/
|
||||||
|
public String toFormattedString(String indentationPattern) {
|
||||||
|
if (indentationPattern == null) {
|
||||||
|
mIndentationPattern = DEFAULT_INDENTATION;
|
||||||
|
} else {
|
||||||
|
mIndentationPattern = indentationPattern;
|
||||||
|
}
|
||||||
|
return toFormattedString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the Json with indentation and line breaks.
|
||||||
|
* Uses the last intendation pattern used, or the default one (3 spaces)
|
||||||
|
*
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
public String toFormattedString() {
|
||||||
|
if (mJsonObject != null) {
|
||||||
|
String indent = "";
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("{\n");
|
||||||
|
format(mJsonObject, builder, indent);
|
||||||
|
builder.append("}\n");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void format(JSONObject jsonObject, StringBuilder builder, String indent) {
|
||||||
|
Iterator<String> keys = jsonObject.keys();
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
String key = keys.next();
|
||||||
|
builder.append(indent);
|
||||||
|
builder.append(mIndentationPattern);
|
||||||
|
builder.append("\"");
|
||||||
|
builder.append(key);
|
||||||
|
builder.append("\": ");
|
||||||
|
Object value = jsonObject.opt(key);
|
||||||
|
if (value instanceof JSONObject) {
|
||||||
|
JSONObject child = (JSONObject) value;
|
||||||
|
builder.append(indent);
|
||||||
|
builder.append("{\n");
|
||||||
|
format(child, builder, indent + mIndentationPattern);
|
||||||
|
builder.append(indent);
|
||||||
|
builder.append(mIndentationPattern);
|
||||||
|
builder.append("}");
|
||||||
|
} else if (value instanceof JSONArray) {
|
||||||
|
JSONArray array = (JSONArray) value;
|
||||||
|
formatArray(array, builder, indent + mIndentationPattern);
|
||||||
|
} else {
|
||||||
|
formatValue(value, builder);
|
||||||
|
}
|
||||||
|
if (keys.hasNext()) {
|
||||||
|
builder.append(",\n");
|
||||||
|
} else {
|
||||||
|
builder.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void formatArray(JSONArray array, StringBuilder builder, String indent) {
|
||||||
|
builder.append("[\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < array.length(); ++i) {
|
||||||
|
Object element = array.opt(i);
|
||||||
|
if (element instanceof JSONObject) {
|
||||||
|
JSONObject child = (JSONObject) element;
|
||||||
|
builder.append(indent);
|
||||||
|
builder.append(mIndentationPattern);
|
||||||
|
builder.append("{\n");
|
||||||
|
format(child, builder, indent + mIndentationPattern);
|
||||||
|
builder.append(indent);
|
||||||
|
builder.append(mIndentationPattern);
|
||||||
|
builder.append("}");
|
||||||
|
} else if (element instanceof JSONArray) {
|
||||||
|
JSONArray child = (JSONArray) element;
|
||||||
|
formatArray(child, builder, indent + mIndentationPattern);
|
||||||
|
} else {
|
||||||
|
builder.append(indent);
|
||||||
|
builder.append(mIndentationPattern);
|
||||||
|
formatValue(element, builder);
|
||||||
|
}
|
||||||
|
if (i < array.length() - 1) {
|
||||||
|
builder.append(",");
|
||||||
|
}
|
||||||
|
builder.append("\n");
|
||||||
|
}
|
||||||
|
builder.append(indent);
|
||||||
|
builder.append("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void formatValue(Object value, StringBuilder builder) {
|
||||||
|
if (value instanceof String) {
|
||||||
|
String string = (String) value;
|
||||||
|
|
||||||
|
// Escape special characters
|
||||||
|
string = string.replaceAll("\\\\", "\\\\\\\\"); // escape backslash
|
||||||
|
string = string.replaceAll("\"", Matcher.quoteReplacement("\\\"")); // escape double quotes
|
||||||
|
string = string.replaceAll("/", "\\\\/"); // escape slash
|
||||||
|
string = string.replaceAll("\n", "\\\\n").replaceAll("\t", "\\\\t"); // escape \n and \t
|
||||||
|
string = string.replaceAll("\r", "\\\\r"); // escape \r
|
||||||
|
|
||||||
|
builder.append("\"");
|
||||||
|
builder.append(string);
|
||||||
|
builder.append("\"");
|
||||||
|
} else if (value instanceof Long) {
|
||||||
|
Long longValue = (Long) value;
|
||||||
|
builder.append(longValue);
|
||||||
|
} else if (value instanceof Integer) {
|
||||||
|
Integer intValue = (Integer) value;
|
||||||
|
builder.append(intValue);
|
||||||
|
} else if (value instanceof Boolean) {
|
||||||
|
Boolean bool = (Boolean) value;
|
||||||
|
builder.append(bool);
|
||||||
|
} else if (value instanceof Double) {
|
||||||
|
Double db = (Double) value;
|
||||||
|
builder.append(db);
|
||||||
|
} else {
|
||||||
|
builder.append(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
154
fizz-common/src/test/java/we/xml/XmlTests.java
Normal file
154
fizz-common/src/test/java/we/xml/XmlTests.java
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package we.xml;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class XmlTests {
|
||||||
|
|
||||||
|
private String xmlStr = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<library>\n"
|
||||||
|
+ " <book id=\"007\">James Bond</book>\n" + "</library>";
|
||||||
|
|
||||||
|
private String jsonStr1 = "{\"library\":{\"book\":{\"#text\":\"James Bond\",\"-id\":\"007\"}}}";
|
||||||
|
private String jsonStr2 = "{\n"
|
||||||
|
+ " \"library\": {\n"
|
||||||
|
+ " \"owner\": \"John Doe\",\n"
|
||||||
|
+ " \"book\": [\n"
|
||||||
|
+ " \"James Bond\",\n"
|
||||||
|
+ " \"Book for the dummies\"\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}";
|
||||||
|
|
||||||
|
private String jsonStr3 = "{\n"
|
||||||
|
+ " \"library\": {\n"
|
||||||
|
+ " \"book\": [\n"
|
||||||
|
+ " \"James Bond\",\n"
|
||||||
|
+ " \"Book for the dummies\"\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}";
|
||||||
|
|
||||||
|
private String jsonStr4 = "{\n"
|
||||||
|
+ " \"library\": {\n"
|
||||||
|
+ " \"owner\": \"John Doe\",\n"
|
||||||
|
+ " \"book\": [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"-id\": \"007\",\n"
|
||||||
|
+ " \"#text\": \"James Bond\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"Book for the dummies\"\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}";
|
||||||
|
|
||||||
|
private String jsonStr5 = "{\n"
|
||||||
|
+ " \"library\": {\n"
|
||||||
|
+ " \"owner\": \"John Doe\",\n"
|
||||||
|
+ " \"book\": [\n"
|
||||||
|
+ " \"1\",\n"
|
||||||
|
+ " \"2\"\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestXmlToJson() {
|
||||||
|
XmlToJson xmlToJson = new XmlToJson.Builder(xmlStr).build();
|
||||||
|
String jsonStr = xmlToJson.toString();
|
||||||
|
|
||||||
|
// System.out.println(jsonStr);
|
||||||
|
|
||||||
|
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||||
|
|
||||||
|
assertEquals("007", jsonObj.getJSONObject("library").getJSONObject("book").getString("-id"));
|
||||||
|
assertEquals("James Bond", jsonObj.getJSONObject("library").getJSONObject("book").getString("#text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestXmlToJsonForceList() {
|
||||||
|
XmlToJson xmlToJson = new XmlToJson.Builder(xmlStr).forceList("/library/book").build();
|
||||||
|
String jsonStr = xmlToJson.toString();
|
||||||
|
|
||||||
|
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||||
|
|
||||||
|
assertEquals("007", jsonObj.getJSONObject("library").getJSONArray("book").getJSONObject(0).getString("-id"));
|
||||||
|
assertEquals("James Bond", jsonObj.getJSONObject("library").getJSONArray("book").getJSONObject(0).getString("#text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestJsonToXml1() {
|
||||||
|
JsonToXml jsonToXml = new JsonToXml.Builder(jsonStr1).build();
|
||||||
|
|
||||||
|
XmlToJson xmlToJson = new XmlToJson.Builder(jsonToXml.toString()).forceList("/library/book").build();
|
||||||
|
String jsonStr = xmlToJson.toString();
|
||||||
|
|
||||||
|
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||||
|
|
||||||
|
assertEquals("007", jsonObj.getJSONObject("library").getJSONArray("book").getJSONObject(0).getString("-id"));
|
||||||
|
assertEquals("James Bond", jsonObj.getJSONObject("library").getJSONArray("book").getJSONObject(0).getString("#text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestJsonToXml2() {
|
||||||
|
JsonToXml jsonToXml = new JsonToXml.Builder(jsonStr2).build();
|
||||||
|
|
||||||
|
XmlToJson xmlToJson = new XmlToJson.Builder(jsonToXml.toString()).build();
|
||||||
|
String jsonStr = xmlToJson.toString();
|
||||||
|
|
||||||
|
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||||
|
|
||||||
|
// System.out.println(xmlToJson.toFormattedString());
|
||||||
|
assertEquals("John Doe", jsonObj.getJSONObject("library").getString("owner"));
|
||||||
|
assertEquals("James Bond", jsonObj.getJSONObject("library").getJSONArray("book").get(0).toString());
|
||||||
|
assertEquals("Book for the dummies", jsonObj.getJSONObject("library").getJSONArray("book").get(1).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestJsonToXml3() {
|
||||||
|
JsonToXml jsonToXml = new JsonToXml.Builder(jsonStr3).build();
|
||||||
|
|
||||||
|
XmlToJson xmlToJson = new XmlToJson.Builder(jsonToXml.toString()).build();
|
||||||
|
String jsonStr = xmlToJson.toString();
|
||||||
|
|
||||||
|
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||||
|
|
||||||
|
assertEquals("James Bond", jsonObj.getJSONObject("library").getJSONArray("book").get(0).toString());
|
||||||
|
assertEquals("Book for the dummies", jsonObj.getJSONObject("library").getJSONArray("book").get(1).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestJsonToXml4() {
|
||||||
|
JsonToXml jsonToXml = new JsonToXml.Builder(jsonStr4).build();
|
||||||
|
|
||||||
|
XmlToJson xmlToJson = new XmlToJson.Builder(jsonToXml.toString()).build();
|
||||||
|
String jsonStr = xmlToJson.toString();
|
||||||
|
|
||||||
|
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||||
|
|
||||||
|
// System.out.println(xmlToJson.toFormattedString());
|
||||||
|
assertEquals("007", jsonObj.getJSONObject("library").getJSONArray("book").getJSONObject(0).getString("-id"));
|
||||||
|
assertEquals("James Bond", jsonObj.getJSONObject("library").getJSONArray("book").getJSONObject(0).getString("#text"));
|
||||||
|
assertEquals("Book for the dummies", jsonObj.getJSONObject("library").getJSONArray("book").get(1).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestJsonToXml5() {
|
||||||
|
JsonToXml jsonToXml = new JsonToXml.Builder(jsonStr5).build();
|
||||||
|
|
||||||
|
XmlToJson xmlToJson = new XmlToJson.Builder(jsonToXml.toString()).forceIntegerForPath("/library/book").build();
|
||||||
|
String jsonStr = xmlToJson.toString();
|
||||||
|
|
||||||
|
JSONObject jsonObj = new JSONObject(jsonStr);
|
||||||
|
|
||||||
|
// System.out.println(xmlToJson.toFormattedString());
|
||||||
|
assertEquals("John Doe", jsonObj.getJSONObject("library").getString("owner"));
|
||||||
|
Object val = jsonObj.getJSONObject("library").getJSONArray("book").get(0);
|
||||||
|
assertTrue(val instanceof Integer);
|
||||||
|
assertEquals(1, jsonObj.getJSONObject("library").getJSONArray("book").getInt(0));
|
||||||
|
assertEquals(2, jsonObj.getJSONObject("library").getJSONArray("book").getInt(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
7
pom.xml
7
pom.xml
@@ -351,6 +351,13 @@
|
|||||||
<artifactId>bcpkix-jdk15on</artifactId>
|
<artifactId>bcpkix-jdk15on</artifactId>
|
||||||
<version>1.64</version>
|
<version>1.64</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.kxml</groupId>
|
||||||
|
<artifactId>kxml2</artifactId>
|
||||||
|
<version>2.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user