This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 3da5b6b8aed CAMEL-18345: camel-jacksonxml - Allow to enable/disable more features via FromXmlParser.Feature enum. Thanks to Jose Bustamante for reporting. 3da5b6b8aed is described below commit 3da5b6b8aed05f37e3499d99a68b6bfd878ff73b Author: Claus Ibsen <claus.ib...@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"); + } + }; + } + +}