diff --git a/src/main/java/com/norconex/commons/lang/bean/BeanMapper.java b/src/main/java/com/norconex/commons/lang/bean/BeanMapper.java index dfb324a3..e7baedf0 100644 --- a/src/main/java/com/norconex/commons/lang/bean/BeanMapper.java +++ b/src/main/java/com/norconex/commons/lang/bean/BeanMapper.java @@ -61,7 +61,6 @@ import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; -import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; @@ -73,14 +72,14 @@ import com.norconex.commons.lang.ClassUtil; import com.norconex.commons.lang.bean.jackson.EmptyWithClosingTagXmlFactory; import com.norconex.commons.lang.bean.jackson.JsonXmlCollectionModule; -import com.norconex.commons.lang.bean.jackson.JsonXmlPropertiesDeserializer; +import com.norconex.commons.lang.bean.jackson.JsonXmlMapModule; +import com.norconex.commons.lang.bean.jackson.JsonXmlPropertiesModule; import com.norconex.commons.lang.bean.spi.PolymorphicTypeLoader; import com.norconex.commons.lang.bean.spi.PolymorphicTypeProvider; import com.norconex.commons.lang.config.Configurable; import com.norconex.commons.lang.convert.GenericJsonModule; import com.norconex.commons.lang.flow.FlowMapperConfig; import com.norconex.commons.lang.flow.module.FlowModule; -import com.norconex.commons.lang.map.Properties; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; @@ -163,9 +162,6 @@ public enum Format { .setProperty( XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); - m.registerModule(new SimpleModule().addDeserializer( - Properties.class, - new JsonXmlPropertiesDeserializer())); return m; }), JSON( @@ -479,6 +475,8 @@ public ObjectMapper toObjectMapper(Format format) { // misc: if (format == Format.XML) { mapper.registerModule(new JsonXmlCollectionModule()); + mapper.registerModule(new JsonXmlMapModule()); + mapper.registerModule(new JsonXmlPropertiesModule()); } mapper.addMixIn(Object.class, NonDefaultInclusionMixIn.class); diff --git a/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMap.java b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMap.java new file mode 100644 index 00000000..a0eb2ed5 --- /dev/null +++ b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMap.java @@ -0,0 +1,85 @@ +/* Copyright 2023 Norconex Inc. + * + * 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 com.norconex.commons.lang.bean.jackson; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.HashMap; + +import com.norconex.commons.lang.bean.BeanMapper; + +/** + *

+ * This annotation must be used with the {@link JsonXmlMapModule}. + * The module is registered automatically for XML source if you are using + * {@link BeanMapper}. + * Use this annotation only when you wish to overwrite default settings + * via its attributes. It is otherwise of no use. + *

+ * @since 3.0.0 + */ +@Retention(RUNTIME) +@Target({ TYPE, FIELD }) +public @interface JsonXmlMap { + + /** + * Field name to use for each map entries when serializing as XML. + * This name does not affect reading. Default is "entry". + * @return entry field name, when writing XML + */ + public String entryName() default "entry"; + + /** + * Field name to use for each map entry keys when serializing as XML. + * Default is "key". + * @return entry key field name, when writing XML + */ + public String keyName() default "key"; + + /** + * Field name to use for each map entry values when serializing as XML. + * Default is "value". + * @return entry value field name, when writing XML + */ + public String valueName() default "value"; + + /** + * Concrete Map type to use when deserializing. Has to be assignable + * to the type of your Map property. Default will try to detect and + * use {@link HashMap} as fallback. + * @return map concrete type + */ + public Class mapType() default Void.class; + + /** + * Concrete key type to use when deserializing. Has to be assignable + * to the type of your Map entry key property. + * Default will try to detect. + * @return map entry key concrete type + */ + public Class keyType() default Void.class; + + /** + * Concrete value type to use when deserializing. Has to be assignable + * to the type of your Map entry value property. + * Default will try to detect. + * @return map entry value concrete type + */ + public Class valueType() default Void.class; +} diff --git a/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapDeserializer.java b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapDeserializer.java new file mode 100644 index 00000000..933f53b4 --- /dev/null +++ b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapDeserializer.java @@ -0,0 +1,159 @@ +/* Copyright 2024 Norconex Inc. + * + * 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 com.norconex.commons.lang.bean.jackson; + +import static org.apache.commons.lang3.StringUtils.defaultIfBlank; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.norconex.commons.lang.ClassUtil; + +/** + * Deserializes a list of "entry" elements each having a "key" and "value" + * child elements into a Map. + * @param Map concrete type + */ +public class JsonXmlMapDeserializer> + extends StdDeserializer + implements ContextualDeserializer { + + private static final long serialVersionUID = 1L; + + private transient BeanProperty currentProperty; + private JavaType mapType; + + public JsonXmlMapDeserializer() { + this((Class) null); + } + + public JsonXmlMapDeserializer(Class vc) { + super(vc); + } + + private JsonXmlMapDeserializer( + BeanProperty currentProperty, JavaType mapType) { + super(Map.class); + this.currentProperty = currentProperty; + this.mapType = mapType; + } + + @Override + public StdDeserializer createContextual( + DeserializationContext ctxt, BeanProperty property) { + return new JsonXmlMapDeserializer<>( + property, + property != null + ? property.getType() + : ctxt.constructType(Map.class)); + } + + @SuppressWarnings("unchecked") + @Override + public T deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException { + var map = createMap(); + var keyName = "key"; + var valueName = "value"; + Class keyType = null; + Class valueType = null; + if (currentProperty != null) { + var annot = currentProperty.getAnnotation(JsonXmlMap.class); + if (annot != null) { + keyName = defaultIfBlank(annot.keyName(), keyName); + valueName = defaultIfBlank(annot.valueName(), valueName); + if (!Void.class.equals(annot.keyType())) { + keyType = annot.keyType(); + } + if (!Void.class.equals(annot.valueType())) { + valueType = annot.valueType(); + } + } + } + + while (p.nextToken() != JsonToken.END_OBJECT) { + + if (p.getCurrentToken() == JsonToken.START_OBJECT) { + Object key = null; + Object value = null; + + while (p.nextToken() != JsonToken.END_OBJECT) { + var fieldName = p.currentName(); + p.nextToken(); // Move to the value of the field + + if (keyName.equals(fieldName)) { + key = ctxt.readValue(p, + fieldType(keyType, mapType.containedType(0))); + } else if (valueName.equals(fieldName)) { + value = ctxt.readValue(p, + fieldType(valueType, mapType.containedType(1))); + } + } + + if (key != null) { + map.put(key, value); + } + } + } + return (T) map; + } + + private Class fieldType(Class annotType, JavaType containedType) { + if (annotType != null) { + return annotType; + } + if (containedType != null) { + return containedType.getRawClass(); + } + return null; + } + + @SuppressWarnings("unchecked") + private Map createMap() { + // Map type established in this priority order: + // - specified on annotation + // - actual type detected and instantiable + // - Fall-back to HashMap + + // from annotation + if (currentProperty != null) { + var annot = currentProperty.getAnnotation(JsonXmlMap.class); + if (annot != null && !Void.class.equals(annot.mapType())) { + return (Map) ClassUtil.newInstance( + annot.mapType()); + } + } + + // from actual type + try { + var map = ClassUtil.newInstance(mapType.getRawClass()); + if (map != null) { + return (Map) map; + } + } catch (Exception e) { + // swallow + } + + return ClassUtil.newInstance(HashMap.class); + } +} diff --git a/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapModule.java b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapModule.java new file mode 100644 index 00000000..ed6a766a --- /dev/null +++ b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapModule.java @@ -0,0 +1,38 @@ +/* Copyright 2023-2024 Norconex Inc. + * + * 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 com.norconex.commons.lang.bean.jackson; + +import java.util.Map; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.norconex.commons.lang.bean.BeanMapper; + +/** + * Jackson module providing (de)serializers for Map, so they can be + * written and read as XML without special hacks. + * Already registered in {@link BeanMapper}. + * @since 3.0.0 + */ +public class JsonXmlMapModule extends SimpleModule { + + private static final long serialVersionUID = 1L; + + @SuppressWarnings("unchecked") + public JsonXmlMapModule() { + this.addSerializer((Class>) (Class) Map.class, + new JsonXmlMapSerializer<>()); + this.addDeserializer(Map.class, new JsonXmlMapDeserializer<>()); + } +} \ No newline at end of file diff --git a/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapSerializer.java b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapSerializer.java new file mode 100644 index 00000000..6e48c203 --- /dev/null +++ b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapSerializer.java @@ -0,0 +1,96 @@ +/* Copyright 2024 Norconex Inc. + * + * 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 com.norconex.commons.lang.bean.jackson; + +import static org.apache.commons.lang3.StringUtils.defaultIfBlank; + +import java.io.IOException; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +/** + * Serializes Map as a list of "entry" elements each having a "key" and "value" + * child elements. Jackson default serialization of Maps stores the key + * as the tag name, and it prevents special characters to be used as keys. + * @param Map concrete type + * @see JsonXmlMap + */ +public class JsonXmlMapSerializer> + extends StdSerializer implements ContextualSerializer { + + private static final long serialVersionUID = 1L; + + private transient BeanProperty currentProperty; + + @SuppressWarnings("unchecked") + public JsonXmlMapSerializer() { + super((Class) Map.class); + } + + @SuppressWarnings("unchecked") + private JsonXmlMapSerializer(BeanProperty currentProperty) { + super((Class) Map.class); + this.currentProperty = currentProperty; + } + + @Override + public StdSerializer createContextual( + SerializerProvider prov, BeanProperty property) + throws JsonMappingException { + return new JsonXmlMapSerializer<>(property); + } + + @Override + public void serialize( + T map, + JsonGenerator gen, + SerializerProvider provider) throws IOException { + + var entryName = "entry"; + var keyName = "key"; + var valueName = "value"; + var annot = currentProperty.getAnnotation(JsonXmlMap.class); + if (annot != null) { + keyName = defaultIfBlank(annot.keyName(), keyName); + valueName = defaultIfBlank(annot.valueName(), valueName); + entryName = defaultIfBlank(annot.entryName(), entryName); + } + gen.writeStartObject(); // outter name + + for (Map.Entry entry : map.entrySet()) { + gen.writeFieldName(entryName); + + gen.writeStartObject(); // + + // Write key + gen.writeFieldName(keyName); + provider.defaultSerializeValue(entry.getKey(), gen); + + // Write value + gen.writeFieldName(valueName); + provider.defaultSerializeValue(entry.getValue(), gen); + + // Close the entry element manually + gen.writeEndObject(); // + } + gen.writeEndObject(); // + } +} diff --git a/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlPropertiesModule.java b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlPropertiesModule.java new file mode 100644 index 00000000..813cecaa --- /dev/null +++ b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlPropertiesModule.java @@ -0,0 +1,36 @@ +/* Copyright 2023-2024 Norconex Inc. + * + * 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 com.norconex.commons.lang.bean.jackson; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.norconex.commons.lang.bean.BeanMapper; +import com.norconex.commons.lang.map.Properties; + +/** + * Jackson module providing (de)serializers for {@link Properties}, so they + * can be written and read as XML without special hacks. + * Already registered in {@link BeanMapper} for XML. + * @since 3.0.0 + */ +public class JsonXmlPropertiesModule extends SimpleModule { + + private static final long serialVersionUID = 1L; + + public JsonXmlPropertiesModule() { + this.addSerializer(Properties.class, new JsonXmlPropertiesSerializer()); + this.addDeserializer(Properties.class, + new JsonXmlPropertiesDeserializer()); + } +} \ No newline at end of file diff --git a/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlPropertiesSerializer.java b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlPropertiesSerializer.java new file mode 100644 index 00000000..034d10c3 --- /dev/null +++ b/src/main/java/com/norconex/commons/lang/bean/jackson/JsonXmlPropertiesSerializer.java @@ -0,0 +1,47 @@ +/* Copyright 2024 Norconex Inc. + * + * 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 com.norconex.commons.lang.bean.jackson; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.norconex.commons.lang.bean.BeanMapper; +import com.norconex.commons.lang.map.Properties; + +/** + * XML serializer for {@link Properties}. + * Already registered in {@link BeanMapper}. + */ +class JsonXmlPropertiesSerializer extends JsonSerializer { + + @Override + public void serialize( + Properties props, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeStartObject(); + for (String key : props.keySet()) { + gen.writeFieldName(key); + gen.writeStartArray(); + for (String val : props.getStrings(key)) { + gen.writeString(val); + } + gen.writeEndArray(); + + } + gen.writeEndObject(); + } +} \ No newline at end of file diff --git a/src/main/java/com/norconex/commons/lang/url/URLStreamer.java b/src/main/java/com/norconex/commons/lang/url/URLStreamer.java index 061313ae..3796529c 100644 --- a/src/main/java/com/norconex/commons/lang/url/URLStreamer.java +++ b/src/main/java/com/norconex/commons/lang/url/URLStreamer.java @@ -25,8 +25,6 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.AutoCloseInputStream; import org.apache.commons.lang3.time.StopWatch; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.norconex.commons.lang.security.Credentials; diff --git a/src/test/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapTest.java b/src/test/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapTest.java new file mode 100644 index 00000000..b2e1d244 --- /dev/null +++ b/src/test/java/com/norconex/commons/lang/bean/jackson/JsonXmlMapTest.java @@ -0,0 +1,132 @@ +/* Copyright 2024 Norconex Inc. + * + * 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 com.norconex.commons.lang.bean.jackson; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; + +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.commons.collections4.map.LinkedMap; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.norconex.commons.lang.bean.BeanMapper; +import com.norconex.commons.lang.bean.BeanMapper.Format; +import com.norconex.commons.lang.map.MapUtil; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +class JsonXmlMapTest { + + private static MapHolder ch; + + @BeforeAll + static void beforeAll() { + ch = new MapHolder(); + ch.some = "thing"; + ch.defaultEntryNames.putAll(MapUtil.toMap("k1", "v1", "k2", "v2")); + ch.specifiedEntryNames = new LinkedMap<>(MapUtil.toMap("k3", "v3", "k4", "v4")); + ch.defaultType = new HashMap<>(MapUtil.toMap(5, true, 6, false)); + ch.complexType.putAll(MapUtil.toMap( + new SomeKey("k7-a", "k7-b"), new SomeValue("v7-a", "b7-b"), + new SomeKey("k8-a", "k8-b"), new SomeValue("v8-a", "b8-b"), + new SomeKey("k9-a", "k9-b"), new SomeValue("v9-a", "b9-b") + )); + } + + @Test + void testWriteRead() { + assertThatNoException().isThrownBy( + () -> BeanMapper.DEFAULT.assertWriteRead(ch, Format.XML)); + } + + @Test + void testXmlTagNames() { + var out = new StringWriter(); + BeanMapper.DEFAULT.write(ch, out, Format.XML); + var xml = out.toString(); + + assertThat(xml).containsIgnoringWhitespaces(""" + + k1v1 + k2v2 + + """); + } + + @Test + void testNullMap() { + assertThatNoException().isThrownBy(() -> { + var out = new StringWriter(); + var obj = new ObjectWithNullMap(); + obj.nullMap = null; + BeanMapper.DEFAULT.write(obj, out, Format.XML); + BeanMapper.DEFAULT.write(obj, out, Format.JSON); + BeanMapper.DEFAULT.write(obj, out, Format.YAML); + }); + } + + @Data + static class MapHolder { + + private String some; + + private final Map defaultEntryNames = new TreeMap<>(); + + @JsonXmlMap( + entryName = "child", + keyName = "childKey", + valueName = "childValue") + private Map specifiedEntryNames; + + private Map defaultType; + + private final Map complexType = new HashMap<>(); + } + + @Data + @Accessors(chain = true) + @AllArgsConstructor + @NoArgsConstructor + public static class SomeKey { + private String propa; + private String propb; + } + + @Data + @Accessors(chain = true) + @AllArgsConstructor + @NoArgsConstructor + public static class SomeValue { + private String propc; + private String propd; + } + + + @Data + @JsonInclude(value = Include.ALWAYS, content = Include.ALWAYS) + static class ObjectWithNullMap { + private Map nullMap = null; + } +} diff --git a/src/test/java/com/norconex/commons/lang/flow/FlowTest.java b/src/test/java/com/norconex/commons/lang/flow/FlowTest.java index a8120b01..22b98893 100644 --- a/src/test/java/com/norconex/commons/lang/flow/FlowTest.java +++ b/src/test/java/com/norconex/commons/lang/flow/FlowTest.java @@ -97,7 +97,6 @@ void testFlowWriteRead(Format format) throws IOException { beanMapper.assertWriteRead(cfg, format); } - @SuppressWarnings("unchecked") private Consumer createPropertiesFlowAsRead() { return Consumers.of( diff --git a/src/test/java/com/norconex/commons/lang/map/PropertiesTest.java b/src/test/java/com/norconex/commons/lang/map/PropertiesTest.java index 974699e6..a1371a76 100644 --- a/src/test/java/com/norconex/commons/lang/map/PropertiesTest.java +++ b/src/test/java/com/norconex/commons/lang/map/PropertiesTest.java @@ -68,19 +68,19 @@ void testWriteRead() throws IOException { props.add("ccc", 555); assertThatNoException().isThrownBy(() -> { - BeanMapper.DEFAULT.assertWriteRead(props);//, Format.JSON, Format.YAML); + BeanMapper.DEFAULT.assertWriteRead(props); }); } @Test - void testValueList() throws IOException { + void testValueList() { var properties = sampleProps(); assertThat(properties.valueList()).contains( "1", "2", "3", "4", "5", "6", "7", "8", "9"); } @Test - void testMiscGetters() throws IOException { + void testMiscGetters() { var props = new Properties(); props.add("bool", "1", "0", "true", "FALSE", "tRuE", "yes", "nO", "On", "oFf"); @@ -112,7 +112,7 @@ void testMiscGetters() throws IOException { } @Test - void testNumberGetters() throws IOException { + void testNumberGetters() { var props = new Properties(); props.add("numbers", "1", "2", "3"); @@ -143,7 +143,7 @@ void testNumberGetters() throws IOException { } @Test - void testDateGetters() throws IOException { + void testDateGetters() { var props = new Properties(); props.add("localDateTime", "2022-11-06T16:20:02", "2023-11-06T16:20:02"); @@ -180,7 +180,7 @@ void testDateGetters() throws IOException { } @Test - void testSetValue() throws IOException { + void testSetValue() { var properties = new Properties(); properties.set("a", "1", "2", "3"); properties.set("a", "4", "5", "6"); @@ -188,7 +188,7 @@ void testSetValue() throws IOException { } @Test - void testSetAddList() throws IOException { + void testSetAddList() { var properties = new Properties(); properties.set("a", "1", "2", "3"); properties.setList("a", null); @@ -200,7 +200,7 @@ void testSetAddList() throws IOException { } @Test - void testToJavaUtilProperties() throws IOException { + void testToJavaUtilProperties() { var properties = sampleProps(); var javaProps = properties.toProperties(); @@ -302,7 +302,7 @@ void testMatch() { } @Test - void testLoadUsingDefaultDelimiter() throws Exception { + void testLoadUsingDefaultDelimiter() { var key = "source"; var value = "X^2"; @@ -320,7 +320,7 @@ void testLoadUsingDefaultDelimiter() throws Exception { } @Test - void testGetList() throws Exception { + void testGetList() { var properties = new Properties(); List list = asList("1", "2", "3"); properties.put("key", list); @@ -336,7 +336,7 @@ void testGetList() throws Exception { } @Test - void testGetValue() throws Exception { + void testGetValue() { var properties = new Properties(MapUtil.toMap( "key", asList("1", "2", "3"))); assertEquals((Integer) 1, properties.get("key", Integer.class)); @@ -350,7 +350,7 @@ void testGetValue() throws Exception { } @Test - void testRemove() throws Exception { + void testRemove() { var properties = new Properties(); List list = asList("a", "b", "c"); properties.put("key", list); @@ -358,7 +358,7 @@ void testRemove() throws Exception { } @Test - void testRemoveCaseInsensitive() throws Exception { + void testRemoveCaseInsensitive() { var properties = new Properties(true); List list = asList("a", "b", "c"); properties.put("KEY", list); @@ -366,7 +366,7 @@ void testRemoveCaseInsensitive() throws Exception { } @Test - void testRemoveCaseInsensitiveMultiple() throws Exception { + void testRemoveCaseInsensitiveMultiple() { var properties = new Properties(true); List list1 = asList("a", "b", "c"); List list2 = asList("d", "e", "f"); @@ -376,19 +376,19 @@ void testRemoveCaseInsensitiveMultiple() throws Exception { } @Test - void testRemoveNonExistingKey() throws Exception { + void testRemoveNonExistingKey() { var properties = new Properties(); assertNull(properties.remove("key")); } @Test - void testRemoveNonExistingKeyCaseInsensitive() throws Exception { + void testRemoveNonExistingKeyCaseInsensitive() { var properties = new Properties(true); assertNull(properties.remove("key")); } @Test - void testAddDifferentCharacterCases() throws Exception { + void testAddDifferentCharacterCases() { var properties = new Properties(true); properties.add("KEY", "value1"); properties.add("key", "value2"); @@ -398,7 +398,7 @@ void testAddDifferentCharacterCases() throws Exception { } @Test - void testPutAll() throws Exception { + void testPutAll() { Map> m = new TreeMap<>(); m.put("KEY", Arrays.asList("1", "2")); m.put("key", Arrays.asList("3", "4")); @@ -422,7 +422,7 @@ void testPutAll() throws Exception { } @Test - void testPut() throws Exception { + void testPut() { List list = Arrays.asList("1", null, "2", ""); // Case insensitive