001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.model.dataformat;
018
019import java.util.Arrays;
020import java.util.List;
021import java.util.Map;
022import javax.xml.bind.annotation.XmlAccessType;
023import javax.xml.bind.annotation.XmlAccessorType;
024import javax.xml.bind.annotation.XmlAttribute;
025import javax.xml.bind.annotation.XmlList;
026import javax.xml.bind.annotation.XmlRootElement;
027
028import org.apache.camel.CamelContext;
029import org.apache.camel.model.DataFormatDefinition;
030import org.apache.camel.spi.DataFormat;
031import org.apache.camel.spi.Metadata;
032
033/**
034 * xml-json data format
035 *
036 * @version 
037 */
038@Metadata(label = "dataformat,transformation", title = "XML JSon")
039@XmlRootElement(name = "xmljson")
040@XmlAccessorType(XmlAccessType.FIELD)
041public class XmlJsonDataFormat extends DataFormatDefinition {
042    
043    public static final String TYPE_HINTS = "typeHints";
044    public static final String REMOVE_NAMESPACE_PREFIXES = "removeNamespacePrefixes";
045    public static final String SKIP_NAMESPACES = "skipNamespaces";
046    public static final String TRIM_SPACES = "trimSpaces";
047    public static final String SKIP_WHITESPACE = "skipWhitespace";
048    public static final String EXPANDABLE_PROPERTIES = "expandableProperties";
049    public static final String ARRAY_NAME = "arrayName";
050    public static final String ELEMENT_NAME = "elementName";
051    public static final String ROOT_NAME = "rootName";
052    public static final String NAMESPACE_LENIENT = "namespaceLenient";
053    public static final String FORCE_TOP_LEVEL_OBJECT = "forceTopLevelObject";
054    public static final String ENCODING = "encoding";
055    
056    @XmlAttribute
057    private String encoding;
058    @XmlAttribute
059    private String elementName;
060    @XmlAttribute
061    private String arrayName;
062    @XmlAttribute
063    private Boolean forceTopLevelObject;
064    @XmlAttribute
065    private Boolean namespaceLenient;
066    @XmlAttribute
067    private String rootName;
068    @XmlAttribute
069    private Boolean skipWhitespace;
070    @XmlAttribute
071    private Boolean trimSpaces;
072    @XmlAttribute
073    private Boolean skipNamespaces;
074    @XmlAttribute
075    private Boolean removeNamespacePrefixes;
076    @XmlAttribute @XmlList
077    private List<String> expandableProperties;
078    @XmlAttribute
079    private String typeHints;
080
081    public XmlJsonDataFormat() {
082        super("xmljson");
083    }
084
085    public XmlJsonDataFormat(Map<String, String> options) {
086        super("xmljson");
087        if (options.containsKey(ENCODING)) {
088            encoding = options.get(ENCODING);
089        }
090        if (options.containsKey(FORCE_TOP_LEVEL_OBJECT)) {
091            forceTopLevelObject = Boolean.parseBoolean(options.get(FORCE_TOP_LEVEL_OBJECT));
092        }
093        if (options.containsKey(NAMESPACE_LENIENT)) {
094            namespaceLenient = Boolean.parseBoolean(options.get(NAMESPACE_LENIENT));
095        }
096        if (options.containsKey(ROOT_NAME)) {
097            rootName = options.get(ROOT_NAME);
098        }
099        if (options.containsKey(ELEMENT_NAME)) {
100            elementName = options.get(ELEMENT_NAME);
101        }
102        if (options.containsKey(ARRAY_NAME)) {
103            arrayName = options.get(ARRAY_NAME);
104        }
105        if (options.containsKey(EXPANDABLE_PROPERTIES)) {
106            expandableProperties = Arrays.asList(options.get(EXPANDABLE_PROPERTIES).split(" "));
107        }
108        if (options.containsKey(SKIP_WHITESPACE)) {
109            skipWhitespace = Boolean.parseBoolean(options.get(SKIP_WHITESPACE));
110        }
111        if (options.containsKey(TRIM_SPACES)) {
112            trimSpaces = Boolean.parseBoolean(options.get(TRIM_SPACES));
113        }
114        if (options.containsKey(SKIP_NAMESPACES)) {
115            skipNamespaces = Boolean.parseBoolean(options.get(SKIP_NAMESPACES));
116        }
117        if (options.containsKey(REMOVE_NAMESPACE_PREFIXES)) {
118            removeNamespacePrefixes = Boolean.parseBoolean(options.get(REMOVE_NAMESPACE_PREFIXES));
119        }
120        if (options.containsKey(TYPE_HINTS)) {
121            typeHints = options.get(TYPE_HINTS);
122        }
123    }
124
125    @Override
126    protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) {
127        if (encoding != null) {
128            setProperty(camelContext, dataFormat, ENCODING, encoding);
129        }
130
131        if (forceTopLevelObject != null) {
132            setProperty(camelContext, dataFormat, FORCE_TOP_LEVEL_OBJECT, forceTopLevelObject);
133        }
134
135        if (namespaceLenient != null) {
136            setProperty(camelContext, dataFormat, NAMESPACE_LENIENT, namespaceLenient);
137        }
138
139        if (rootName != null) {
140            setProperty(camelContext, dataFormat, ROOT_NAME, rootName);
141        }
142        
143        if (elementName != null) {
144            setProperty(camelContext, dataFormat, ELEMENT_NAME, elementName);
145        }
146
147        if (arrayName != null) {
148            setProperty(camelContext, dataFormat, ARRAY_NAME, arrayName);
149        }
150
151        if (expandableProperties != null && expandableProperties.size() != 0) {
152            setProperty(camelContext, dataFormat, EXPANDABLE_PROPERTIES, expandableProperties);
153        }
154
155        if (skipWhitespace != null) {
156            setProperty(camelContext, dataFormat, SKIP_WHITESPACE, skipWhitespace);
157        }
158
159        if (trimSpaces != null) {
160            setProperty(camelContext, dataFormat, TRIM_SPACES, trimSpaces);
161        }
162
163        if (skipNamespaces != null) {
164            setProperty(camelContext, dataFormat, SKIP_NAMESPACES, skipNamespaces);
165        }
166
167        if (removeNamespacePrefixes != null) {
168            setProperty(camelContext, dataFormat, REMOVE_NAMESPACE_PREFIXES, removeNamespacePrefixes);
169        }
170
171        // will end up calling the setTypeHints(String s) which does the parsing from the Enum String key to the Enum value
172        if (typeHints != null) {
173            setProperty(camelContext, dataFormat, TYPE_HINTS, typeHints);
174        }
175
176        //TODO: xmljson: element-namespace mapping is not implemented in the XML DSL
177        // depending on adoption rate of this data format, we'll make this data format NamespaceAware so that it gets
178        // the prefix-namespaceURI mappings from the context, and with a new attribute called "namespacedElements",
179        // we'll associate named elements with prefixes following a format "element1:prefix1,element2:prefix2,..."
180    }
181
182    public String getEncoding() {
183        return encoding;
184    }
185
186    /**
187     * Sets the encoding.
188     * Used for unmarshalling (JSON to XML conversion).
189     */
190    public void setEncoding(String encoding) {
191        this.encoding = encoding;
192    }
193
194    public String getElementName() {
195        return elementName;
196    }
197
198    /**
199     * Specifies the name of the XML elements representing each array element.
200     * Used for unmarshalling (JSON to XML conversion).
201     */
202    public void setElementName(String elementName) {
203        this.elementName = elementName;
204    }
205
206    public String getArrayName() {
207        return arrayName;
208    }
209
210    /**
211     * Specifies the name of the top-level XML element.
212     * Used for unmarshalling (JSON to XML conversion).
213     *
214     * For example, when converting [1, 2, 3], it will be output by default as <a><e>1</e><e>2</e><e>3</e></a>.
215     * By setting this option or rootName, you can alter the name of element 'a'.
216     */
217    public void setArrayName(String arrayName) {
218        this.arrayName = arrayName;
219    }
220
221    public Boolean getForceTopLevelObject() {
222        return forceTopLevelObject;
223    }
224
225    /**
226     * Determines whether the resulting JSON will start off with a top-most element whose name matches the XML root element.
227     * Used for marshalling (XML to JSon conversion).
228     *
229     * If disabled, XML string <a><x>1</x><y>2</y></a> turns into { 'x: '1', 'y': '2' }.
230     * Otherwise, it turns into { 'a': { 'x: '1', 'y': '2' }}.
231     */
232    public void setForceTopLevelObject(Boolean forceTopLevelObject) {
233        this.forceTopLevelObject = forceTopLevelObject;
234    }
235
236    public Boolean getNamespaceLenient() {
237        return namespaceLenient;
238    }
239
240    /**
241     * Flag to be tolerant to incomplete namespace prefixes.
242     * Used for unmarshalling (JSON to XML conversion).
243     * In most cases, json-lib automatically changes this flag at runtime to match the processing.
244     */
245    public void setNamespaceLenient(Boolean namespaceLenient) {
246        this.namespaceLenient = namespaceLenient;
247    }
248
249    public String getRootName() {
250        return rootName;
251    }
252
253    /**
254     * Specifies the name of the top-level element.
255     * Used for unmarshalling (JSON to XML conversion).
256     *
257     * If not set, json-lib will use arrayName or objectName (default value: 'o', at the current time it is not configurable in this data format).
258     * If set to 'root', the JSON string { 'x': 'value1', 'y' : 'value2' } would turn
259     * into <root><x>value1</x><y>value2</y></root>, otherwise the 'root' element would be named 'o'.
260     */
261    public void setRootName(String rootName) {
262        this.rootName = rootName;
263    }
264
265    public Boolean getSkipWhitespace() {
266        return skipWhitespace;
267    }
268
269    /**
270     * Determines whether white spaces between XML elements will be regarded as text values or disregarded.
271     * Used for marshalling (XML to JSon conversion).
272     */
273    public void setSkipWhitespace(Boolean skipWhitespace) {
274        this.skipWhitespace = skipWhitespace;
275    }
276
277    public Boolean getTrimSpaces() {
278        return trimSpaces;
279    }
280
281    /**
282     * Determines whether leading and trailing white spaces will be omitted from String values.
283     * Used for marshalling (XML to JSon conversion).
284     */
285    public void setTrimSpaces(Boolean trimSpaces) {
286        this.trimSpaces = trimSpaces;
287    }
288
289    public Boolean getSkipNamespaces() {
290        return skipNamespaces;
291    }
292
293    /**
294     * Signals whether namespaces should be ignored. By default they will be added to the JSON output using @xmlns elements.
295     * Used for marshalling (XML to JSon conversion).
296     */
297    public void setSkipNamespaces(Boolean skipNamespaces) {
298        this.skipNamespaces = skipNamespaces;
299    }
300
301    public Boolean getRemoveNamespacePrefixes() {
302        return removeNamespacePrefixes;
303    }
304
305    /**
306     * Removes the namespace prefixes from XML qualified elements, so that the resulting JSON string does not contain them.
307     * Used for marshalling (XML to JSon conversion).
308     */
309    public void setRemoveNamespacePrefixes(Boolean removeNamespacePrefixes) {
310        this.removeNamespacePrefixes = removeNamespacePrefixes;
311    }
312
313    public List<String> getExpandableProperties() {
314        return expandableProperties;
315    }
316
317    /**
318     * With expandable properties, JSON array elements are converted to XML as a sequence of repetitive XML elements
319     * with the local name equal to the JSON key, for example: { number: 1,2,3 }, normally converted to:
320     * <number><e>1</e><e>2</e><e>3</e></number> (where e can be modified by setting elementName), would instead
321     * translate to <number>1</number><number>2</number><number>3</number>, if "number" is set as an expandable property
322     * Used for unmarshalling (JSON to XML conversion).
323     */
324    public void setExpandableProperties(List<String> expandableProperties) {
325        this.expandableProperties = expandableProperties;
326    }
327
328    public String getTypeHints() {
329        return typeHints;
330    }
331
332    /**
333     * Adds type hints to the resulting XML to aid conversion back to JSON.
334     * Used for unmarshalling (JSON to XML conversion).
335     */
336    public void setTypeHints(String typeHints) {
337        this.typeHints = typeHints;
338    }
339
340}