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

net.sf.practicalxml.converter.CollectionConverter Maven / Gradle / Ivy

// Copyright 2008-2014 severally by the contributors
//
// 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 net.sf.practicalxml.converter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import net.sf.practicalxml.DomUtil;


/**
 *  Converts between an XML DOM and hierarchical Java collections, according
 *  to the following rules:
 *  

* From DOM to Collection: *

    *
  • The top-level Element is transformed into Map. *
  • The keys of this map will be the local names of the child * elements, sans prefix. *
  • Children that have text content will map to String values. *
  • Children that have element content will map to Map values, * and processed recursively. *
  • Mixed content is not permitted; if an element contains mixed content, * the text nodes will be discarded. *
  • If multiple child elements have the same name, they will map to a * List, which in turn contains either strings or maps. *
  • Empty elements are added to the map with a value of null. *
  • Order of keys is not preserved, but order of repeated elements is. *
*

* From Collection to DOM: *

    *
  • A Map is converted to an element with children, where each * key in the map becomes an Element. *
  • String values are turned into text nodes under the key * element. *
  • Map values are turned into elements recursively. *
  • List and Set values are turned into repeated * elements with the same key (and may be in turn either strings or maps). *
*

* Each of the conversion functions allows the caller to specify a list of * keys as a filter. If these keys are present, only the specified keys will * be processed from the input. * * @since 1.1.3 */ public class CollectionConverter { /** * Creates a new DOM document from the passed map, without any namespace. * * @param map The source object. Its elements will be the children * of the document root. * @param rootName The local name given to the root element of the * generated document. * @param keyFilter If present, the mappings will be limited to child * elements with the specified names. */ public static Document convertToXml( Map map, String rootName, String... keyFilter) { Element root = DomUtil.newDocument(rootName); appendElements(map, root, digestFilter(keyFilter)); return root.getOwnerDocument(); } /** * Creates a new DOM document from the passed map, in which all elements * are members of the specified namespace and will inherit the root's * prefix (if any). * * @param map The source object. Its elements will be the children * of the document root. * @param rootName The qualified name given to the root element of the * generated document (this is a QName to * avoid ambiguous argument lists). * @param keyFilter If present, the mappings will be limited to child * elements with the specified names. */ public static Document convertToXml( Map map, QName rootName, String... keyFilter) { Element root = DomUtil.newDocument(rootName); appendElements(map, root, digestFilter(keyFilter)); return root.getOwnerDocument(); } /** * Converts a single XML element into a map. * * @param elem The element. * @param keyFilter If present, the mappings will be limited to child * elements with the specified names. */ public static Map convertToMap(Element elem, String... keyFilter) { return convertToMap(elem, digestFilter(keyFilter)); } /** * Converts a list of XML elements into a list of maps. * * @param elems The elements. * @param keyFilter If present, the mappings will be limited to child * elements with the specified names. */ public static List> convertToMap(List elems, String... keyFilter) { List> result = new ArrayList>(elems.size()); Set filter = digestFilter(keyFilter); for (Element elem : elems) result.add(convertToMap(elem, filter)); return result; } //---------------------------------------------------------------------------- // Internals //---------------------------------------------------------------------------- /** * Converts the varargs filter into set, null if no filter provided. */ private static Set digestFilter(String... keyFilter) { if (keyFilter.length == 0) return null; Set filter = new HashSet(); for (String key : keyFilter) filter.add(key); return filter; } /** * Common conversion code for a single element, with digested filter. * * @param elem The element. * @param keyFilter If not null contains the child element * names that will be put in the map. */ public static Map convertToMap(Element elem, Set keyFilter) { Map result = new HashMap(); for (Element child : DomUtil.getChildren(elem)) appendChild(result, child, keyFilter); return result; } private static void appendChild(Map map, Element child, Set keyFilter) { String key = DomUtil.getLocalName(child); if ((keyFilter != null) && !keyFilter.contains(key)) return; Object value = getChildValue(child, keyFilter); if (!map.containsKey(key)) { map.put(key, value); return; } Object current = map.get(key); if (current instanceof List) { List list = (List)current; list.add(value); } else { List list = new ArrayList(); list.add(current); list.add(value); map.put(key, list); } } private static Object getChildValue(Element child, Set keyFilter) { if (DomUtil.hasElementChildren(child)) return convertToMap(child, keyFilter); else return DomUtil.getText(child); } private static void appendElements(Map map, Element parent, Set keyFilter) { for (Map.Entry entry : map.entrySet()) { String key = entry.getKey(); if ((keyFilter != null) && !keyFilter.contains(key)) continue; Object value = entry.getValue(); appendElement(parent, key, value, keyFilter); } } private static void appendElement(Element parent, String key, Object value, Set keyFilter) { if (value == null) { DomUtil.appendChildInheritNamespace(parent, key); } else if (value instanceof String) { Element child = DomUtil.appendChildInheritNamespace(parent, key); DomUtil.setText(child, (String)value); } else if (value instanceof Map) { Element child = DomUtil.appendChildInheritNamespace(parent, key); appendElements((Map)value, child, keyFilter); } else if (value instanceof Collection) { for (Object obj : (Collection)value) appendElement(parent, key, obj, keyFilter); } else if (value.getClass().isArray()) { for (Object obj : (Object[])value) appendElement(parent, key, obj, keyFilter); } } }