All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dslplatform.json.XmlConverter Maven / Gradle / Ivy

There is a newer version: 1.36.0
Show newest version
package com.dslplatform.json;

import org.w3c.dom.*;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.*;

public abstract class XmlConverter {

	static final JsonReader.ReadObject Reader = new JsonReader.ReadObject() {
		@Nullable
		@Override
		public Element read(JsonReader reader) throws IOException {
			return reader.wasNull() ? null : deserialize(reader);
		}
	};
	static final JsonWriter.WriteObject Writer = new JsonWriter.WriteObject() {
		@Override
		public void write(JsonWriter writer, @Nullable Element value) {
			serializeNullable(value, writer);
		}
	};

	private static final DocumentBuilder documentBuilder;

	static {
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		try {
			documentBuilder = dbFactory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			throw new RuntimeException(e);
		}
	}

	public static void serializeNullable(@Nullable final Element value, final JsonWriter sw) {
		if (value == null)
			sw.writeNull();
		else
			serialize(value, sw);
	}

	public static void serialize(final Element value, final JsonWriter sw) {
		Document document = value.getOwnerDocument();
		DOMImplementationLS domImplLS = (DOMImplementationLS) document.getImplementation();
		LSSerializer serializer = domImplLS.createLSSerializer();
		LSOutput lsOutput = domImplLS.createLSOutput();
		lsOutput.setEncoding("UTF-8");
		StringWriter writer = new StringWriter();
		lsOutput.setCharacterStream(writer);
		serializer.write(document, lsOutput);
		StringConverter.serialize(writer.toString(), sw);
	}

	public static Element deserialize(final JsonReader reader) throws IOException {
		if (reader.last() == '"') {
			try {
				InputSource source = new InputSource(new StringReader(reader.readString()));
				return documentBuilder.parse(source).getDocumentElement();
			} catch (SAXException ex) {
				throw reader.newParseErrorAt("Invalid XML value", 0, ex);
			}
		} else {
			final Map map = ObjectConverter.deserializeMap(reader);
			return mapToXml(map);
		}
	}

	public static Element mapToXml(final Map map) throws IOException {
		final Set xmlRootElementNames = map.keySet();
		if (xmlRootElementNames.size() > 1) {
			throw ParsingException.create("Invalid XML. Expecting root element", true);
		}
		final String rootName = xmlRootElementNames.iterator().next();
		final Document document = createDocument();
		final Element rootElement = document.createElement(rootName);
		document.appendChild(rootElement);
		buildXmlFromHashMap(document, rootElement, map.get(rootName));
		return rootElement;
	}

	private static synchronized Document createDocument() {
		try {
			final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			final DocumentBuilder builder = factory.newDocumentBuilder();
			return builder.newDocument();
		} catch (ParserConfigurationException e) {
			throw new ConfigurationException(e);
		}
	}

	private static final String TEXT_NODE_TAG = "#text";
	private static final String COMMENT_NODE_TAG = "#comment";
	private static final String CDATA_NODE_TAG = "#cdata-section";

	@SuppressWarnings("unchecked")
	private static void buildXmlFromHashMap(
			final Document doc,
			final Element subtreeRootElement,
			@Nullable final Object elementContent) {
		if (elementContent instanceof HashMap) {
			final HashMap elementContentMap = (HashMap) elementContent;
			for (final Map.Entry childEntry : elementContentMap.entrySet()) {
				final String key = childEntry.getKey();
				if (key.startsWith("@")) {
					subtreeRootElement.setAttribute(key.substring(1), childEntry.getValue().toString());
				} else if (key.startsWith("#")) {
					if (key.equals(TEXT_NODE_TAG)) {
						if (childEntry.getValue() instanceof List) {
							buildTextNodeList(doc, subtreeRootElement, (List) childEntry.getValue());
						} else {
							final Node textNode = doc.createTextNode(childEntry.getValue().toString());
							subtreeRootElement.appendChild(textNode);
						}
					} else if (key.equals(CDATA_NODE_TAG)) {
						if (childEntry.getValue() instanceof List) {
							buildCDataList(doc, subtreeRootElement, (List) childEntry.getValue());
						} else {
							final Node cDataNode = doc.createCDATASection(childEntry.getValue().toString());
							subtreeRootElement.appendChild(cDataNode);
						}
					} else if (key.equals(COMMENT_NODE_TAG)) {
						if (childEntry.getValue() instanceof List) {
							buildCommentList(doc, subtreeRootElement, (List) childEntry.getValue());
						} else {
							final Node commentNode = doc.createComment(childEntry.getValue().toString());
							subtreeRootElement.appendChild(commentNode);
						}
					} //else if (key.equals(WHITESPACE_NODE_TAG)
					//	|| key.equals(SIGNIFICANT_WHITESPACE_NODE_TAG)) {
					// Ignore
					//} else {
						/*
						 * All other nodes whose name starts with a '#' are invalid XML
						 * nodes, and thus ignored:
						 */
					//}
				} else {
					final Element newElement = doc.createElement(key);
					subtreeRootElement.appendChild(newElement);
					buildXmlFromHashMap(doc, newElement, childEntry.getValue());
				}
			}
		} else if (elementContent instanceof List) {
			buildXmlFromJsonArray(doc, subtreeRootElement, (List) elementContent);
		} else {
			if (elementContent != null) {
				subtreeRootElement.setTextContent(elementContent.toString());
			}
		}
	}

	private static void buildTextNodeList(final Document doc, final Node subtreeRoot, final List nodeValues) {
		final StringBuilder sb = new StringBuilder();
		for (final String nodeValue : nodeValues) {
			sb.append(nodeValue);
		}
		subtreeRoot.appendChild(doc.createTextNode(sb.toString()));
	}

	private static void buildCDataList(final Document doc, final Node subtreeRoot, final List nodeValues) {
		for (final String nodeValue : nodeValues) {
			subtreeRoot.appendChild(doc.createCDATASection(nodeValue));
		}
	}

	private static void buildCommentList(final Document doc, final Node subtreeRoot, final List nodeValues) {
		for (final String nodeValue : nodeValues) {
			subtreeRoot.appendChild(doc.createComment(nodeValue));
		}
	}

	private static void buildXmlFromJsonArray(
			final Document doc,
			final Node listHeadNode,
			final List elementContentList) {
		final Node subtreeRootNode = listHeadNode.getParentNode();
		/* The head node (already exists) */
		buildXmlFromHashMap(doc, (Element) listHeadNode, elementContentList.get(0));
		/* The rest of the list */
		for (final Object elementContent : elementContentList.subList(1, elementContentList.size())) {
			final Element newElement = doc.createElement(listHeadNode.getNodeName());
			subtreeRootNode.appendChild(newElement);
			buildXmlFromHashMap(doc, newElement, elementContent);
		}
	}

	@SuppressWarnings("unchecked")
	public static ArrayList deserializeCollection(final JsonReader reader) throws IOException {
		return reader.deserializeCollection(Reader);
	}

	public static void deserializeCollection(final JsonReader reader, final Collection res) throws IOException {
		reader.deserializeCollection(Reader, res);
	}

	@SuppressWarnings("unchecked")
	public static ArrayList deserializeNullableCollection(final JsonReader reader) throws IOException {
		return reader.deserializeNullableCollection(Reader);
	}

	public static void deserializeNullableCollection(final JsonReader reader, final Collection res) throws IOException {
		reader.deserializeNullableCollection(Reader, res);
	}
}