From 46ff530412ba4626a62f2867b5a6b3662c8624c5 Mon Sep 17 00:00:00 2001 From: M66B Date: Mon, 29 Apr 2024 11:45:03 +0200 Subject: [PATCH] Update Bugsnag/DSL-JSON --- .../dslplatform/json/BinaryConverter.java | 4 +- .../dslplatform/json/BoolConverter.java | 4 +- .../repackaged/dslplatform/json/DslJson.java | 16 +- .../json/ExternalConverterAnalyzer.java | 9 +- .../dslplatform/json/JsonReader.java | 10 +- .../dslplatform/json/MapConverter.java | 4 +- .../dslplatform/json/NetConverter.java | 8 +- .../dslplatform/json/NumberConverter.java | 22 +- .../dslplatform/json/ObjectConverter.java | 4 +- .../dslplatform/json/StringConverter.java | 4 +- .../dslplatform/json/UUIDConverter.java | 4 +- .../dslplatform/json/XmlConverter.java | 217 ++++++++++++++++++ 12 files changed, 266 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/XmlConverter.java diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/BinaryConverter.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/BinaryConverter.java index 34fbc2949f..d3bbe44ef6 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/BinaryConverter.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/BinaryConverter.java @@ -39,7 +39,7 @@ public abstract class BinaryConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(Base64Reader); + return reader.deserializeCollectionCustom(Base64Reader); } public static void deserializeCollection(final JsonReader reader, final Collection res) throws IOException { @@ -48,7 +48,7 @@ public abstract class BinaryConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(Base64Reader); + return reader.deserializeNullableCollectionCustom(Base64Reader); } public static void deserializeNullableCollection(final JsonReader reader, final Collection res) throws IOException { diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/BoolConverter.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/BoolConverter.java index cd8b7e41cc..40dfdd298f 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/BoolConverter.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/BoolConverter.java @@ -110,7 +110,7 @@ public abstract class BoolConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(READER); + return reader.deserializeCollectionCustom(READER); } public static void deserializeCollection(final JsonReader reader, final Collection res) throws IOException { @@ -119,7 +119,7 @@ public abstract class BoolConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(READER); + return reader.deserializeNullableCollectionCustom(READER); } public static void deserializeNullableCollection(final JsonReader reader, final Collection res) throws IOException { diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/DslJson.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/DslJson.java index d703b00bab..d917c829ec 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/DslJson.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/DslJson.java @@ -804,7 +804,7 @@ public class DslJson implements UnknownSerializer, TypeLookup { for (ClassLoader loader : loaders) { try { Class external = loader.loadClass(name); - Configuration instance = (Configuration) external.newInstance(); + Configuration instance = (Configuration) external.getDeclaredConstructor().newInstance(); instance.configure(json); } catch (NoClassDefFoundError ignore) { } catch (Exception ignore) { @@ -813,6 +813,8 @@ public class DslJson implements UnknownSerializer, TypeLookup { } static void registerJavaSpecifics(final DslJson json) { + json.registerReader(Element.class, XmlConverter.Reader); + json.registerWriter(Element.class, XmlConverter.Writer); } private final Map defaults = new ConcurrentHashMap(); @@ -1653,7 +1655,7 @@ public class DslJson implements UnknownSerializer, TypeLookup { } final JsonReader.ReadObject contentReader = tryFindReader(content); if (contentReader != null) { - final ArrayList result = json.deserializeNullableCollection(contentReader); + final ArrayList result = json.deserializeNullableCollectionCustom(contentReader); if (container.isArray()) { return returnAsArray(content, result); } @@ -1673,7 +1675,7 @@ public class DslJson implements UnknownSerializer, TypeLookup { } final JsonReader.ReadObject contentReader = tryFindReader(content); if (contentReader != null) { - final ArrayList result = json.deserializeNullableCollection(contentReader); + final ArrayList result = json.deserializeNullableCollectionCustom(contentReader); return returnAsArray(content, result); } } @@ -1768,7 +1770,7 @@ public class DslJson implements UnknownSerializer, TypeLookup { } final JsonReader.ReadObject simpleReader = tryFindReader(manifest); if (simpleReader != null) { - return json.deserializeNullableCollection(simpleReader); + return json.deserializeNullableCollectionCustom(simpleReader); } if (fallback != null) { final Object array = Array.newInstance(manifest, 0); @@ -1883,7 +1885,7 @@ public class DslJson implements UnknownSerializer, TypeLookup { } final JsonReader.ReadObject simpleReader = tryFindReader(manifest); if (simpleReader != null) { - return json.deserializeNullableCollection(simpleReader); + return json.deserializeNullableCollectionCustom(simpleReader); } if (fallback != null) { final Object array = Array.newInstance(manifest, 0); @@ -2009,7 +2011,7 @@ public class DslJson implements UnknownSerializer, TypeLookup { } final JsonReader.ReadObject simpleElementReader = tryFindReader(elementManifest); if (simpleElementReader != null) { - List list = json.deserializeNullableCollection(simpleElementReader); + List list = json.deserializeNullableCollectionCustom(simpleElementReader); return (TResult) convertResultToArray(elementManifest, list); } } @@ -2267,7 +2269,7 @@ public class DslJson implements UnknownSerializer, TypeLookup { } final JsonReader.ReadObject simpleReader = tryFindReader(manifest); if (simpleReader != null) { - return json.iterateOver(simpleReader); + return json.iterateOverCustom(simpleReader); } if (fallback != null) { final Object array = Array.newInstance(manifest, 0); diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/ExternalConverterAnalyzer.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/ExternalConverterAnalyzer.java index 4364a91e8a..0e7b75aacc 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/ExternalConverterAnalyzer.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/ExternalConverterAnalyzer.java @@ -1,5 +1,6 @@ package com.bugsnag.android.repackaged.dslplatform.json; +import java.lang.reflect.InvocationTargetException; import java.util.*; class ExternalConverterAnalyzer { @@ -19,14 +20,16 @@ class ExternalConverterAnalyzer { try { Class converterClass = cl.loadClass(ccn); if (!Configuration.class.isAssignableFrom(converterClass)) continue; - Configuration converter = (Configuration) converterClass.newInstance(); + Configuration converter = (Configuration) converterClass.getDeclaredConstructor().newInstance(); converter.configure(dslJson); return true; } catch (ClassNotFoundException ignored) { } catch (IllegalAccessException ignored) { } catch (InstantiationException ignored) { - } - } + } catch (InvocationTargetException e) { + } catch (NoSuchMethodException e) { + } + } } return false; } diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/JsonReader.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/JsonReader.java index addb14a6c2..ba26388c76 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/JsonReader.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/JsonReader.java @@ -1594,12 +1594,13 @@ public final class JsonReader { return res.toArray(emptyArray); } - public final ArrayList deserializeCollection(final ReadObject readObject) throws IOException { + public final ArrayList deserializeCollectionCustom(final ReadObject readObject) throws IOException { final ArrayList res = new ArrayList(4); deserializeCollection(readObject, res); return res; } + @SuppressWarnings("overloads") public final void deserializeCollection(final ReadObject readObject, final Collection res) throws IOException { res.add(readObject.read(this)); while (getNextToken() == ',') { @@ -1609,12 +1610,13 @@ public final class JsonReader { checkArrayEnd(); } - public final ArrayList deserializeNullableCollection(final ReadObject readObject) throws IOException { + public final ArrayList deserializeNullableCollectionCustom(final ReadObject readObject) throws IOException { final ArrayList res = new ArrayList(4); deserializeNullableCollection(readObject, res); return res; } + @SuppressWarnings("overloads") public final void deserializeNullableCollection(final ReadObject readObject, final Collection res) throws IOException { if (wasNull()) { res.add(null); @@ -1638,6 +1640,7 @@ public final class JsonReader { return res; } + @SuppressWarnings("overloads") public final void deserializeCollection(final ReadJsonObject readObject, final Collection res) throws IOException { if (last == '{') { getNextToken(); @@ -1658,6 +1661,7 @@ public final class JsonReader { return res; } + @SuppressWarnings("overloads") public final void deserializeNullableCollection(final ReadJsonObject readObject, final Collection res) throws IOException { if (last == '{') { getNextToken(); @@ -1676,7 +1680,7 @@ public final class JsonReader { checkArrayEnd(); } - public final Iterator iterateOver(final JsonReader.ReadObject reader) { + public final Iterator iterateOverCustom(final JsonReader.ReadObject reader) { return new WithReader(reader, this); } diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/MapConverter.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/MapConverter.java index 5de4221a0c..afc832fb53 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/MapConverter.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/MapConverter.java @@ -70,7 +70,7 @@ public abstract class MapConverter { @SuppressWarnings("unchecked") public static ArrayList> deserializeCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(TypedMapReader); + return reader.deserializeCollectionCustom(TypedMapReader); } public static void deserializeCollection(final JsonReader reader, final Collection> res) throws IOException { @@ -79,7 +79,7 @@ public abstract class MapConverter { @SuppressWarnings("unchecked") public static ArrayList> deserializeNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(TypedMapReader); + return reader.deserializeNullableCollectionCustom(TypedMapReader); } public static void deserializeNullableCollection(final JsonReader reader, final Collection> res) throws IOException { diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/NetConverter.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/NetConverter.java index f496f9ca0b..b01edbea6e 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/NetConverter.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/NetConverter.java @@ -56,7 +56,7 @@ public abstract class NetConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeUriCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(UriReader); + return reader.deserializeCollectionCustom(UriReader); } public static void deserializeUriCollection(final JsonReader reader, final Collection res) throws IOException { @@ -65,7 +65,7 @@ public abstract class NetConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeUriNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(UriReader); + return reader.deserializeNullableCollectionCustom(UriReader); } public static void deserializeUriNullableCollection(final JsonReader reader, final Collection res) throws IOException { @@ -92,7 +92,7 @@ public abstract class NetConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeIpCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(AddressReader); + return reader.deserializeCollectionCustom(AddressReader); } public static void deserializeIpCollection(final JsonReader reader, final Collection res) throws IOException { @@ -101,7 +101,7 @@ public abstract class NetConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeIpNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(AddressReader); + return reader.deserializeNullableCollectionCustom(AddressReader); } public static void deserializeIpNullableCollection(final JsonReader reader, final Collection res) throws IOException { diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/NumberConverter.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/NumberConverter.java index 3369c44775..5d87bab985 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/NumberConverter.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/NumberConverter.java @@ -570,7 +570,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeDoubleCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(DOUBLE_READER); + return reader.deserializeCollectionCustom(DOUBLE_READER); } public static void deserializeDoubleCollection(final JsonReader reader, final Collection res) throws IOException { @@ -579,7 +579,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeDoubleNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(DOUBLE_READER); + return reader.deserializeNullableCollectionCustom(DOUBLE_READER); } public static void deserializeDoubleNullableCollection(final JsonReader reader, final Collection res) throws IOException { @@ -772,7 +772,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeFloatCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(FLOAT_READER); + return reader.deserializeCollectionCustom(FLOAT_READER); } public static void deserializeFloatCollection(final JsonReader reader, Collection res) throws IOException { @@ -781,7 +781,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeFloatNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(FLOAT_READER); + return reader.deserializeNullableCollectionCustom(FLOAT_READER); } public static void deserializeFloatNullableCollection(final JsonReader reader, final Collection res) throws IOException { @@ -981,7 +981,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeIntCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(INT_READER); + return reader.deserializeCollectionCustom(INT_READER); } public static int[] deserializeIntArray(final JsonReader reader) throws IOException { @@ -1080,7 +1080,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeShortNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(SHORT_READER); + return reader.deserializeNullableCollectionCustom(SHORT_READER); } public static void deserializeShortNullableCollection(final JsonReader reader, final Collection res) throws IOException { @@ -1093,7 +1093,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeIntNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(INT_READER); + return reader.deserializeNullableCollectionCustom(INT_READER); } public static void deserializeIntNullableCollection(final JsonReader reader, final Collection res) throws IOException { @@ -1317,7 +1317,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeLongCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(LONG_READER); + return reader.deserializeCollectionCustom(LONG_READER); } public static void deserializeLongCollection(final JsonReader reader, final Collection res) throws IOException { @@ -1326,7 +1326,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeLongNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(LONG_READER); + return reader.deserializeNullableCollectionCustom(LONG_READER); } public static void deserializeLongNullableCollection(final JsonReader reader, final Collection res) throws IOException { @@ -1682,7 +1682,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeDecimalCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(DecimalReader); + return reader.deserializeCollectionCustom(DecimalReader); } public static void deserializeDecimalCollection(final JsonReader reader, final Collection res) throws IOException { @@ -1691,7 +1691,7 @@ public abstract class NumberConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeDecimalNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(DecimalReader); + return reader.deserializeNullableCollectionCustom(DecimalReader); } public static void deserializeDecimalNullableCollection(final JsonReader reader, final Collection res) throws IOException { diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/ObjectConverter.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/ObjectConverter.java index ab37064fe0..2d9ff534a8 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/ObjectConverter.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/ObjectConverter.java @@ -117,7 +117,7 @@ public abstract class ObjectConverter { @SuppressWarnings("unchecked") public static ArrayList> deserializeMapCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(TypedMapReader); + return reader.deserializeCollectionCustom(TypedMapReader); } public static void deserializeMapCollection(final JsonReader reader, final Collection> res) throws IOException { @@ -126,7 +126,7 @@ public abstract class ObjectConverter { @SuppressWarnings("unchecked") public static ArrayList> deserializeNullableMapCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(TypedMapReader); + return reader.deserializeNullableCollectionCustom(TypedMapReader); } public static void deserializeNullableMapCollection(final JsonReader reader, final Collection> res) throws IOException { diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/StringConverter.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/StringConverter.java index 06fe923f5e..f45af88556 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/StringConverter.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/StringConverter.java @@ -89,7 +89,7 @@ public abstract class StringConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(READER); + return reader.deserializeCollectionCustom(READER); } public static void deserializeCollection(final JsonReader reader, final Collection res) throws IOException { @@ -98,7 +98,7 @@ public abstract class StringConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(READER); + return reader.deserializeNullableCollectionCustom(READER); } public static void deserializeNullableCollection(final JsonReader reader, final Collection res) throws IOException { diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/UUIDConverter.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/UUIDConverter.java index 92ef69e925..d5f80b08f6 100644 --- a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/UUIDConverter.java +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/UUIDConverter.java @@ -180,7 +180,7 @@ public abstract class UUIDConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeCollection(final JsonReader reader) throws IOException { - return reader.deserializeCollection(READER); + return reader.deserializeCollectionCustom(READER); } public static void deserializeCollection(final JsonReader reader, final Collection res) throws IOException { @@ -189,7 +189,7 @@ public abstract class UUIDConverter { @SuppressWarnings("unchecked") public static ArrayList deserializeNullableCollection(final JsonReader reader) throws IOException { - return reader.deserializeNullableCollection(READER); + return reader.deserializeNullableCollectionCustom(READER); } public static void deserializeNullableCollection(final JsonReader reader, final Collection res) throws IOException { diff --git a/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/XmlConverter.java b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/XmlConverter.java new file mode 100644 index 0000000000..6c06ee792a --- /dev/null +++ b/app/src/main/java/com/bugsnag/android/repackaged/dslplatform/json/XmlConverter.java @@ -0,0 +1,217 @@ +package com.bugsnag.android.repackaged.dslplatform.json; + +import androidx.annotation.Nullable; + +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.*; + +@SuppressWarnings({"rawtypes", "unchecked"}) // suppress pre-existing warnings +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.deserializeCollectionCustom(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.deserializeNullableCollectionCustom(Reader); + } + + public static void deserializeNullableCollection(final JsonReader reader, final Collection res) throws IOException { + reader.deserializeNullableCollection(Reader, res); + } +}