package com.gustz.dove.cli.api.service.util; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAnyElement; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * * TODO: JAXB XML parser * * @author ZHENFENG ZHANG * @since [ Aug 11, 2015 ] */ public abstract class JaxbParser { private static final String ENCODING = "utf-8"; private static ConcurrentMap<Class<?>, JAXBContext> jaxbContextMap = new ConcurrentHashMap<Class<?>, JAXBContext>(); /** * Bean --> XML * * @param root * @return * @throws JAXBException */ public static String toXml(Object root) throws JAXBException { return toXml(root, ENCODING); } /** * Bean --> XML * * @param root * @param encoding * @return * @throws JAXBException */ public static String toXml(Object root, String encoding) throws JAXBException { return toXml(root, encoding, root.getClass()); } /** * Bean --> XML * * @param root * @param encoding * @param retType * @return * @throws JAXBException */ public static String toXml(Object root, String encoding, Class<?> retType) throws JAXBException { StringWriter writer = new StringWriter(); createMarshaller(retType, encoding).marshal(root, writer); return writer.toString(); } /** * Bean --> XML * * @param root * @param rootName * @param retType * @return * @throws JAXBException */ public static String toXml(Collection<?> root, String rootName, Class<?> retType) throws JAXBException { return toXml(root, rootName, ENCODING, retType); } /** * Bean --> XML * * @param root * @param rootName * @param encoding * @param retType * @return * @throws JAXBException */ public static String toXml(Collection<?> root, String rootName, String encoding, Class<?> retType) throws JAXBException { CollectionWrapper wrapper = new CollectionWrapper(); wrapper.collection = root; JAXBElement<CollectionWrapper> wrapperElement = new JAXBElement<CollectionWrapper>(new QName(rootName), CollectionWrapper.class, wrapper); // StringWriter writer = new StringWriter(); createMarshaller(retType, encoding).marshal(wrapperElement, new StringWriter()); return writer.toString(); } /** * XML --> Bean * * @param xml * @param retType * @return * @throws JAXBException */ @SuppressWarnings("unchecked") public static <T> T fromXml(String xml, Class<T> retType) throws JAXBException { return (T) createUnmarshaller(retType).unmarshal(new StringReader(xml)); } /** * XML --> Bean * * @param xml * @param retType * @return * @throws JAXBException * @throws ParserConfigurationException */ @SuppressWarnings("unchecked") public static <T> T fromXmlByMap(String xml, Class<T> retType) throws JAXBException, ParserConfigurationException { return (T) createUnmarshaller(retType, new InnerMapAdapter()).unmarshal(new StringReader(xml)); } /** * XML --> Bean * * @param xml * @param retType * @param adapter * @return * @throws JAXBException */ @SuppressWarnings("unchecked") public static <T> T fromXml(String xml, Class<T> retType, XmlAdapter<?, ?> adapter) throws JAXBException { return (T) createUnmarshaller(retType, adapter).unmarshal(new StringReader(xml)); } /** * XML --> Bean * * @param ins * @param retType * @return * @throws JAXBException */ @SuppressWarnings("unchecked") public static <T> T fromXml(InputStream ins, Class<T> retType) throws JAXBException { return (T) createUnmarshaller(retType).unmarshal(ins); } /** * XML --> Bean * * @param ins * @param retType * @param adapter * @return * @throws JAXBException */ @SuppressWarnings("unchecked") public static <T> T fromXml(InputStream ins, Class<T> retType, XmlAdapter<?, ?> adapter) throws JAXBException { return (T) createUnmarshaller(retType, adapter).unmarshal(ins); } /** * XML --> Bean * * @param ins * @param retType * @return * @throws JAXBException * @throws ParserConfigurationException */ @SuppressWarnings("unchecked") public static <T> T fromXmlByMap(InputStream ins, Class<T> retType) throws JAXBException, ParserConfigurationException { return (T) createUnmarshaller(retType, new InnerMapAdapter()).unmarshal(ins); } /** * XML --> Bean * * @param reader * @param retType * @return * @throws JAXBException */ @SuppressWarnings("unchecked") public static <T> T fromXml(Reader reader, Class<T> retType) throws JAXBException { return (T) createUnmarshaller(retType).unmarshal(reader); } /** * create JAXB marshaller * * @param retType * @param encoding * @param decl * @param isFormat * @param adapter * @return * @throws JAXBException */ public static Marshaller createMarshaller(Class<?> retType, String encoding, String decl, boolean isFormat, XmlAdapter<?, ?> adapter) throws JAXBException { Marshaller marshaller = getJaxbContext(retType).createMarshaller(); if (isFormat) { marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); } if (encoding != null && !encoding.isEmpty()) { marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding); } if (decl != null && !decl.isEmpty()) { marshaller.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE); } marshaller.setAdapter(adapter); return marshaller; } private static Marshaller createMarshaller(Class<?> retType, String encoding) throws JAXBException { Marshaller marshaller = getJaxbContext(retType).createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); if (encoding != null && !encoding.isEmpty()) { marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding); } return marshaller; } private static Unmarshaller createUnmarshaller(Class<?> retType) throws JAXBException { return getJaxbContext(retType).createUnmarshaller(); } private static Unmarshaller createUnmarshaller(Class<?> retType, XmlAdapter<?, ?> adapter) throws JAXBException { Unmarshaller unm = createUnmarshaller(retType); unm.setAdapter(adapter); return unm; } private static JAXBContext getJaxbContext(Class<?> retType) throws JAXBException { JAXBContext cxt = null; if (jaxbContextMap.containsKey(retType)) { cxt = jaxbContextMap.get(retType); } if (cxt == null) { cxt = JAXBContext.newInstance(retType, CollectionWrapper.class); jaxbContextMap.putIfAbsent(retType, cxt); } return cxt; } private static class CollectionWrapper { @XmlAnyElement protected Collection<?> collection; } } class InnerMapAdapter extends XmlAdapter<InnerMapAdapter.InnerAdaptedMap, Map<String, String>> { private DocumentBuilder docBuilder; public InnerMapAdapter() throws ParserConfigurationException { docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); } static class InnerAdaptedMap { @XmlAnyElement public List<Element> elements = new ArrayList<Element>(); } @Override public InnerAdaptedMap marshal(Map<String, String> map) { Document doc = docBuilder.newDocument(); InnerAdaptedMap adaptedMap = new InnerAdaptedMap(); for (Entry<String, String> _entry : map.entrySet()) { Element el = doc.createElement(_entry.getKey()); el.setTextContent(_entry.getValue()); adaptedMap.elements.add(el); } return adaptedMap; } @Override public Map<String, String> unmarshal(InnerAdaptedMap adaptedMap) { Map<String, String> _map = new HashMap<String, String>(); for (Element element : adaptedMap.elements) { _map.put(element.getLocalName(), element.getTextContent()); } return _map; } }