You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/08/04 14:53:24 UTC

[camel] branch camel-3.18.x updated: CAMEL-18345: camel-jacksonxml - Allow to enable/disable more features via FromXmlParser.Feature enum. Thanks to Jose Bustamante for reporting.

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch camel-3.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-3.18.x by this push:
     new 427acea1104 CAMEL-18345: camel-jacksonxml - Allow to enable/disable more features via FromXmlParser.Feature enum. Thanks to  Jose Bustamante for reporting.
427acea1104 is described below

commit 427acea11048ef112fe4959956eecbc165744254
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Aug 4 16:52:23 2022 +0200

    CAMEL-18345: camel-jacksonxml - Allow to enable/disable more features via FromXmlParser.Feature enum. Thanks to  Jose Bustamante for reporting.
---
 .../src/main/docs/jacksonXml-dataformat.adoc       |  9 ++-
 .../component/jacksonxml/JacksonXMLDataFormat.java | 33 +++++++-
 .../jacksonxml/JacksonEmptyElementsAsNullTest.java | 87 ++++++++++++++++++++++
 3 files changed, 123 insertions(+), 6 deletions(-)

diff --git a/components/camel-jacksonxml/src/main/docs/jacksonXml-dataformat.adoc b/components/camel-jacksonxml/src/main/docs/jacksonXml-dataformat.adoc
index f44f8ca5d8a..69ffa156dba 100644
--- a/components/camel-jacksonxml/src/main/docs/jacksonXml-dataformat.adoc
+++ b/components/camel-jacksonxml/src/main/docs/jacksonXml-dataformat.adoc
@@ -255,8 +255,8 @@ Multiple modules can be specified separated by comma, such as `moduleRefs="myJac
 
 == Enabling or disable features using Jackson
 
-Jackson has a number of features you can enable or disable, which its
-ObjectMapper uses. For example to disable failing on unknown properties
+Jackson XML has a number of features you can enable or disable, which its
+XmlMapper uses. For example to disable failing on unknown properties
 when marshalling, you can configure this using the disableFeatures:
 
 [source,xml]
@@ -273,6 +273,7 @@ from the following enum classes:
 * `com.fasterxml.jackson.databind.SerializationFeature`
 * `com.fasterxml.jackson.databind.DeserializationFeature`
 * `com.fasterxml.jackson.databind.MapperFeature`
+* `com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser.Feature`
 
 To enable a feature use the enableFeatures options instead.
 
@@ -288,7 +289,7 @@ df.disableFeature(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
 
 == Converting Maps to POJO using Jackson
 
-Jackson `ObjectMapper` can be used to convert maps to POJO objects.
+Jackson `XmlMapper` can be used to convert maps to POJO objects.
 Jackson component comes with the data converter that can be used to
 convert `java.util.Map` instance to non-String, non-primitive and
 non-Number objects.
@@ -303,7 +304,7 @@ producerTemplate.sendBody("direct:mapToInvoice", invoiceData);
 Invoice invoice = exchange.getIn().getBody(Invoice.class);
 ----------------------------------------------------------------
 
-If there is a single `ObjectMapper` instance available in the Camel
+If there is a single `XmlMapper` instance available in the Camel
 registry, it will used by the converter to perform the conversion.
 Otherwise the default mapper will be used.  
 
diff --git a/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java b/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java
index 683cdad5b7c..21f3e1629f5 100644
--- a/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java
+++ b/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java
@@ -33,6 +33,7 @@ import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.type.CollectionType;
 import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
 import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
@@ -456,6 +457,14 @@ public class JacksonXMLDataFormat extends ServiceSupport
         }
     }
 
+    public void enableFeature(FromXmlParser.Feature feature) {
+        if (enableFeatures == null) {
+            enableFeatures = feature.name();
+        } else {
+            enableFeatures += "," + feature.name();
+        }
+    }
+
     public void disableFeature(SerializationFeature feature) {
         if (disableFeatures == null) {
             disableFeatures = feature.name();
@@ -480,6 +489,14 @@ public class JacksonXMLDataFormat extends ServiceSupport
         }
     }
 
+    public void disableFeature(FromXmlParser.Feature feature) {
+        if (disableFeatures == null) {
+            disableFeatures = feature.name();
+        } else {
+            disableFeatures += "," + feature.name();
+        }
+    }
+
     @Override
     protected void doInit() throws Exception {
         if (unmarshalTypeName != null && (unmarshalType == null || unmarshalType == HashMap.class)) {
@@ -540,9 +557,15 @@ public class JacksonXMLDataFormat extends ServiceSupport
                     xmlMapper.enable(mf);
                     continue;
                 }
+                FromXmlParser.Feature pf
+                        = getCamelContext().getTypeConverter().tryConvertTo(FromXmlParser.Feature.class, enable);
+                if (pf != null) {
+                    xmlMapper.enable(pf);
+                    continue;
+                }
                 throw new IllegalArgumentException(
                         "Enable feature: " + enable
-                                                   + " cannot be converted to an accepted enum of types [SerializationFeature,DeserializationFeature,MapperFeature]");
+                                                   + " cannot be converted to an accepted enum of types [SerializationFeature,DeserializationFeature,MapperFeature,FromXmlParser.Feature]");
             }
         }
         if (disableFeatures != null) {
@@ -567,9 +590,15 @@ public class JacksonXMLDataFormat extends ServiceSupport
                     xmlMapper.disable(mf);
                     continue;
                 }
+                FromXmlParser.Feature pf
+                        = getCamelContext().getTypeConverter().tryConvertTo(FromXmlParser.Feature.class, disable);
+                if (pf != null) {
+                    xmlMapper.disable(pf);
+                    continue;
+                }
                 throw new IllegalArgumentException(
                         "Disable feature: " + disable
-                                                   + " cannot be converted to an accepted enum of types [SerializationFeature,DeserializationFeature,MapperFeature]");
+                                                   + " cannot be converted to an accepted enum of types [SerializationFeature,DeserializationFeature,MapperFeature,FromXmlParser.Feature]");
             }
         }
 
diff --git a/components/camel-jacksonxml/src/test/java/org/apache/camel/component/jacksonxml/JacksonEmptyElementsAsNullTest.java b/components/camel-jacksonxml/src/test/java/org/apache/camel/component/jacksonxml/JacksonEmptyElementsAsNullTest.java
new file mode 100644
index 00000000000..3d782f3e5d6
--- /dev/null
+++ b/components/camel-jacksonxml/src/test/java/org/apache/camel/component/jacksonxml/JacksonEmptyElementsAsNullTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jacksonxml;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class JacksonEmptyElementsAsNullTest extends CamelTestSupport {
+
+    @Test
+    public void testEmptyAsNull() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(4);
+
+        template.sendBody("direct:start", "<pojo><name>Jack</name></pojo>");
+        template.sendBody("direct:start", "<pojo><name></name></pojo>");
+        template.sendBody("direct:start", "<pojo><name/></pojo>");
+        template.sendBody("direct:start", "<pojo></pojo>");
+
+        assertMockEndpointsSatisfied();
+
+        Assertions.assertEquals("Jack", mock.getReceivedExchanges().get(0).getMessage().getBody(TestPojo.class).getName());
+        // <name></name> and <name/> are NOT the same as empty string vs null
+        Assertions.assertEquals("", mock.getReceivedExchanges().get(1).getMessage().getBody(TestPojo.class).getName());
+        Assertions.assertNull(mock.getReceivedExchanges().get(2).getMessage().getBody(TestPojo.class).getName());
+        Assertions.assertNull(mock.getReceivedExchanges().get(3).getMessage().getBody(TestPojo.class).getName());
+    }
+
+    @Test
+    public void testDefault() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result2");
+        mock.expectedMessageCount(4);
+
+        template.sendBody("direct:start2", "<pojo><name>Jack</name></pojo>");
+        template.sendBody("direct:start2", "<pojo><name></name></pojo>");
+        template.sendBody("direct:start2", "<pojo><name/></pojo>");
+        template.sendBody("direct:start2", "<pojo></pojo>");
+
+        assertMockEndpointsSatisfied();
+
+        Assertions.assertEquals("Jack", mock.getReceivedExchanges().get(0).getMessage().getBody(TestPojo.class).getName());
+        // <name></name> and <name/> are both the same as an empty string
+        Assertions.assertEquals("", mock.getReceivedExchanges().get(1).getMessage().getBody(TestPojo.class).getName());
+        Assertions.assertEquals("", mock.getReceivedExchanges().get(2).getMessage().getBody(TestPojo.class).getName());
+        Assertions.assertNull(mock.getReceivedExchanges().get(3).getMessage().getBody(TestPojo.class).getName());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+
+            @Override
+            public void configure() {
+                context.setStreamCaching(false);
+
+                JacksonXMLDataFormat format = new JacksonXMLDataFormat(TestPojo.class);
+                format.enableFeature(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL);
+                format.disableFeature(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+                from("direct:start").unmarshal(format).to("mock:result");
+
+                JacksonXMLDataFormat format2 = new JacksonXMLDataFormat(TestPojo.class);
+                format.disableFeature(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+                from("direct:start2").unmarshal(format2).to("mock:result2");
+            }
+        };
+    }
+
+}