Author: oheger
Date: Wed Jun 21 20:00:25 2017
New Revision: 1799501

URL: http://svn.apache.org/viewvc?rev=1799501&view=rev
Log:
[CONFIGURATION-258] Support for JSON and YAML configurations.

Thanks to The Alchemist (kap4020 at gmail dot com) for the patch.

Added:
    
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/AbstractMapBasedConfiguration.java
    
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/JSONConfiguration.java
    
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/YAMLConfiguration.java
    
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestJSONConfiguration.java
    
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestYAMLConfiguration.java
    commons/proper/configuration/trunk/src/test/resources/test.json
    commons/proper/configuration/trunk/src/test/resources/test.yaml
Modified:
    commons/proper/configuration/trunk/pom.xml

Modified: commons/proper/configuration/trunk/pom.xml
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/pom.xml?rev=1799501&r1=1799500&r2=1799501&view=diff
==============================================================================
--- commons/proper/configuration/trunk/pom.xml (original)
+++ commons/proper/configuration/trunk/pom.xml Wed Jun 21 20:00:25 2017
@@ -469,6 +469,22 @@
       <version>${slf4j.version}</version>
       <scope>test</scope>
     </dependency>
+
+
+    <dependency>
+      <groupId>org.yaml</groupId>
+      <artifactId>snakeyaml</artifactId>
+      <version>1.18</version>
+      <optional>true</optional>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+      <version>2.5.3</version>
+      <optional>true</optional>
+    </dependency>
   </dependencies>
 
   <properties>

Added: 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/AbstractMapBasedConfiguration.java
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/AbstractMapBasedConfiguration.java?rev=1799501&view=auto
==============================================================================
--- 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/AbstractMapBasedConfiguration.java
 (added)
+++ 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/AbstractMapBasedConfiguration.java
 Wed Jun 21 20:00:25 2017
@@ -0,0 +1,92 @@
+package org.apache.commons.configuration2;
+
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.commons.configuration2.io.ConfigurationLogger;
+import org.apache.commons.configuration2.tree.ImmutableNode;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author The-Alchemist
+ */
+public class AbstractMapBasedConfiguration extends 
BaseHierarchicalConfiguration {
+
+    public AbstractMapBasedConfiguration() {
+        super();
+
+        initLogger(new ConfigurationLogger(getClass()));
+    }
+
+    protected void load(Map<String, Object> map) {
+        ImmutableNode.Builder rootBuilder = new ImmutableNode.Builder();
+        ImmutableNode top = constructHierarchy(rootBuilder, map);
+        getNodeModel().setRootNode(top);
+    }
+
+    /**
+     * Constructs a YAML map, i.e. String -> Object from a given
+     * configuration node.
+     *
+     * @param node The configuration node to create a map from.
+     * @return A Map that contains the configuration node information.
+     */
+    protected Map<String, Object> constructMap(ImmutableNode node)
+    {
+        Map<String, Object> map = new HashMap<String, 
Object>(node.getChildren().size());
+        for (ImmutableNode cNode : node.getChildren())
+        {
+            if (cNode.getChildren().isEmpty())
+            {
+                map.put(cNode.getNodeName(), cNode.getValue());
+            }
+            else
+            {
+                map.put(cNode.getNodeName(), constructMap(cNode));
+            }
+        }
+        return map;
+    }
+
+    /**
+     * Constructs the internal configuration nodes hierarchy.
+     *  @param parent The configuration node that is the root of the current 
configuration section.
+     * @param map The map with the yaml configurations nodes, i.e. String -> 
Object.
+     */
+    protected ImmutableNode constructHierarchy(ImmutableNode.Builder parent, 
Map<String, Object> map)
+    {
+        for (Map.Entry<String, Object> entry : map.entrySet())
+        {
+            String key = entry.getKey();
+            Object value = entry.getValue();
+            if (value instanceof Map)
+            {
+                ImmutableNode.Builder subtree = new ImmutableNode.Builder()
+                        .name(key);
+                ImmutableNode children = constructHierarchy(subtree, (Map) 
value);
+                parent.addChild(children);
+            }
+            else
+            {
+                ImmutableNode leaf = new ImmutableNode.Builder()
+                        .name(key)
+                        .value(value)
+                        .create();
+                parent.addChild(leaf);
+            }
+        }
+        return parent.create();
+    }
+
+
+    static void rethrowException(Exception e) throws ConfigurationException {
+        if(e instanceof ClassCastException)
+        {
+            throw new ConfigurationException("Error parsing", e);
+        }
+        else
+        {
+            throw new ConfigurationException("Unable to load the 
configuration", e);
+        }
+    }
+}

Added: 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/JSONConfiguration.java
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/JSONConfiguration.java?rev=1799501&view=auto
==============================================================================
--- 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/JSONConfiguration.java
 (added)
+++ 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/JSONConfiguration.java
 Wed Jun 21 20:00:25 2017
@@ -0,0 +1,117 @@
+/*
+ * 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.commons.configuration2;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.MapType;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.commons.configuration2.io.InputStreamSupport;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Map;
+
+/**
+ * <p>
+ * A specialized hierarchical configuration class that is able to parse JSON
+ * documents.
+ * </p>
+ *
+ * @author  The-Alchemist
+ *
+ * @since commons-configuration2 2.2.?
+ * @version $$
+ */
+
+public class JSONConfiguration extends AbstractMapBasedConfiguration implements
+        FileBasedConfiguration, InputStreamSupport
+{
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    private final MapType type = 
mapper.getTypeFactory().constructMapType(Map.class, String.class, Object.class);
+
+
+    /**
+     * Creates a new instance of {@code YAMLConfiguration}.
+     */
+    public JSONConfiguration() {
+        super();
+    }
+
+    @Override
+    public void read(Reader in) throws ConfigurationException
+    {
+        try
+        {
+            Map<String, Object> map = mapper.readValue(in, this.type);
+            load(map);
+        }
+        catch (Exception e)
+        {
+            rethrowException(e);
+        }
+    }
+
+    private String readFully(Reader in) {
+        try (BufferedReader r = new BufferedReader(in)) {
+            String str = null;
+            StringBuilder sb = new StringBuilder();
+            while((str =r.readLine())!=null)
+            {
+                sb.append(str);
+            }
+            return sb.toString();
+        } catch(IOException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    @Override
+    public void write(Writer out) throws ConfigurationException, IOException
+    {
+        this.mapper.writer().writeValue(out, 
constructMap(this.getNodeModel().getNodeHandler().getRootNode()));
+    }
+
+
+    /**
+     * Loads the configuration from the given input stream.
+     *
+     * @param in the input stream
+     * @throws ConfigurationException if an error occurs
+     */
+    @Override
+    public void read(InputStream in) throws ConfigurationException
+    {
+        try
+        {
+            Map<String, Object> map = mapper.readValue(in, this.type);
+            load(map);
+        }
+        catch (Exception e)
+        {
+            rethrowException(e);
+        }
+    }
+
+
+
+}
\ No newline at end of file

Added: 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/YAMLConfiguration.java
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/YAMLConfiguration.java?rev=1799501&view=auto
==============================================================================
--- 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/YAMLConfiguration.java
 (added)
+++ 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration2/YAMLConfiguration.java
 Wed Jun 21 20:00:25 2017
@@ -0,0 +1,141 @@
+/*
+ * 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.commons.configuration2;
+
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.commons.configuration2.io.ConfigurationLogger;
+import org.apache.commons.configuration2.io.InputStreamSupport;
+import org.apache.commons.configuration2.tree.ImmutableNode;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.LoaderOptions;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <p>
+ * A specialized hierarchical configuration class that is able to parse YAML
+ * documents.
+ * </p>
+ *
+ * @author  The-Alchemist
+ *
+ * @since commons-configuration2 2.2.?
+ * @version $$
+ */
+
+public class YAMLConfiguration extends AbstractMapBasedConfiguration implements
+        FileBasedConfiguration, InputStreamSupport
+{
+
+    /**
+     * Creates a new instance of {@code YAMLConfiguration}.
+     */
+    public YAMLConfiguration()
+    {
+        super();
+    }
+
+
+    @Override
+    public void read(Reader in) throws ConfigurationException
+    {
+        try
+        {
+            Yaml yaml = new Yaml();
+            Map<String, Object> map = (Map) yaml.load(in);
+            load(map);
+        }
+        catch (Exception e)
+        {
+            rethrowException(e);
+        }
+    }
+
+
+    public void read(Reader in, LoaderOptions options) throws 
ConfigurationException
+    {
+        try
+        {
+            Yaml yaml = new Yaml(options);
+            Map<String, Object> map = (Map) yaml.load(in);
+            load(map);
+        }
+        catch (Exception e)
+        {
+            rethrowException(e);
+        }
+    }
+
+    @Override
+    public void write(Writer out) throws ConfigurationException, IOException
+    {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        dump(out, options);
+    }
+
+    public void dump(Writer out, DumperOptions options) throws 
ConfigurationException, IOException
+    {
+        Yaml yaml = new Yaml(options);
+        yaml.dump(constructMap(getNodeModel().getNodeHandler().getRootNode()), 
out);
+    }
+
+
+    /**
+     * Loads the configuration from the given input stream.
+     *
+     * @param in the input stream
+     * @throws ConfigurationException if an error occurs
+     */
+    @Override
+    public void read(InputStream in) throws ConfigurationException
+    {
+        try
+        {
+            Yaml yaml = new Yaml();
+            Map<String, Object> map = (Map) yaml.load(in);
+            load(map);
+        }
+        catch (Exception e)
+        {
+            rethrowException(e);
+        }
+    }
+
+    public void read(InputStream in, LoaderOptions options) throws 
ConfigurationException
+    {
+        try
+        {
+            Yaml yaml = new Yaml(options);
+            Map<String, Object> map = (Map) yaml.load(in);
+            load(map);
+        }
+        catch (Exception e)
+        {
+            rethrowException(e);
+        }
+    }
+
+
+}
\ No newline at end of file

Added: 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestJSONConfiguration.java
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestJSONConfiguration.java?rev=1799501&view=auto
==============================================================================
--- 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestJSONConfiguration.java
 (added)
+++ 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestJSONConfiguration.java
 Wed Jun 21 20:00:25 2017
@@ -0,0 +1,123 @@
+package org.apache.commons.configuration2;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.MapType;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+/**
+ * Unit test for {@link JSONConfiguration}
+ *
+ * Not ideal: it uses the Jackson JSON parser just like {@link 
JSONConfiguration} itself
+ * @author The-Alchemist
+ */
+public class TestJSONConfiguration {
+    /** The files that we test with. */
+    private String testJson = 
ConfigurationAssert.getTestFile("test.json").getAbsolutePath();
+    private File testSaveConf = 
ConfigurationAssert.getOutFile("testsave.json");
+
+    private JSONConfiguration jsonConfiguration;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        jsonConfiguration = new JSONConfiguration();
+        jsonConfiguration.read(new FileReader(testJson));
+        removeTestFile();
+    }
+
+    @Test
+    public void testGetProperty_simple()
+    {
+        assertEquals("value1", jsonConfiguration.getProperty("key1"));
+    }
+
+    @Test
+    public void testGetProperty_nested()
+    {
+        assertEquals("value23", jsonConfiguration.getProperty("key2.key3"));
+    }
+
+    @Test
+    public void testGetProperty_nested_with_list()
+    {
+        assertEquals(Arrays.asList("col1", "col2"), 
jsonConfiguration.getProperty("key4.key5"));
+    }
+
+    @Test
+    public void testGetProperty_subset()
+    {
+        Configuration subset = jsonConfiguration.subset("key4");
+        assertEquals(Arrays.asList("col1", "col2"), 
subset.getProperty("key5"));
+    }
+
+
+    @Test
+    public void testGetProperty_very_nested_properties()
+    {
+        Object property = 
jsonConfiguration.getProperty("very.nested.properties");
+        assertEquals(Arrays.asList("nested1", "nested2", "nested3"), property);
+    }
+
+    @Test
+    public void testGetProperty_integer()
+    {
+        Object property = jsonConfiguration.getProperty("int1");
+        assertTrue("property should be an Integer", property instanceof 
Integer);
+        assertEquals(37, property);
+    }
+
+    @Test
+    public void testSave() throws IOException, ConfigurationException {
+        // save the Configuration as a String...
+        StringWriter sw = new StringWriter();
+        jsonConfiguration.write(sw);
+        String output = sw.toString();
+
+        // ..and then try parsing it back
+        ObjectMapper mapper = new ObjectMapper();
+        MapType type = mapper.getTypeFactory().constructMapType(Map.class, 
String.class, Object.class);
+        Map<String, Object> parsed = mapper.readValue(output, type);
+        assertEquals(6, parsed.entrySet().size());
+        assertEquals("value1", parsed.get("key1"));
+
+        Map key2 = (Map) parsed.get("key2");
+        assertEquals("value23", key2.get("key3"));
+
+        List<String> key5 = (List<String>) ((Map) 
parsed.get("key4")).get("key5");
+        assertEquals(2, key5.size());
+        assertEquals("col1", key5.get(0));
+        assertEquals("col2", key5.get(1));
+    }
+
+    @Test
+    public void testGetProperty_dictionary()
+    {
+        assertEquals("Martin D'vloper", 
jsonConfiguration.getProperty("martin.name"));
+        assertEquals("Developer", jsonConfiguration.getProperty("martin.job"));
+        assertEquals("Elite", jsonConfiguration.getProperty("martin.skill"));
+    }
+
+    /**
+     * Removes the test output file if it exists.
+     */
+    private void removeTestFile()
+    {
+        if (testSaveConf.exists())
+        {
+            assertTrue(testSaveConf.delete());
+        }
+    }
+
+}
\ No newline at end of file

Added: 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestYAMLConfiguration.java
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestYAMLConfiguration.java?rev=1799501&view=auto
==============================================================================
--- 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestYAMLConfiguration.java
 (added)
+++ 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration2/TestYAMLConfiguration.java
 Wed Jun 21 20:00:25 2017
@@ -0,0 +1,123 @@
+package org.apache.commons.configuration2;
+
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.junit.Before;
+import org.junit.Test;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * Unit test for {@link YAMLConfiguration}
+ *
+ * @author The-Alchemist
+ */
+public class TestYAMLConfiguration
+{
+    /** The files that we test with. */
+    private String testYaml = 
ConfigurationAssert.getTestFile("test.yaml").getAbsolutePath();
+    private File testSaveConf = 
ConfigurationAssert.getOutFile("testsave.yaml");
+
+    private YAMLConfiguration yamlConfiguration;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        yamlConfiguration = new YAMLConfiguration();
+        yamlConfiguration.read(new FileReader(testYaml));
+        removeTestFile();
+    }
+
+    @Test
+    public void testGetProperty_simple()
+    {
+        assertEquals("value1", yamlConfiguration.getProperty("key1"));
+    }
+
+    @Test
+    public void testGetProperty_nested()
+    {
+        assertEquals("value23", yamlConfiguration.getProperty("key2.key3"));
+    }
+
+    @Test
+    public void testGetProperty_nested_with_list()
+    {
+        assertEquals(Arrays.asList("col1", "col2"), 
yamlConfiguration.getProperty("key4.key5"));
+    }
+
+    @Test
+    public void testGetProperty_subset()
+    {
+        Configuration subset = yamlConfiguration.subset("key4");
+        assertEquals(Arrays.asList("col1", "col2"), 
subset.getProperty("key5"));
+    }
+
+
+    @Test
+    public void testGetProperty_very_nested_properties()
+    {
+        Object property = 
yamlConfiguration.getProperty("very.nested.properties");
+        assertEquals(Arrays.asList("nested1", "nested2", "nested3"), property);
+    }
+
+    @Test
+    public void testGetProperty_integer()
+    {
+        Object property = yamlConfiguration.getProperty("int1");
+        assertTrue("property should be an Integer", property instanceof 
Integer);
+        assertEquals(37, property);
+    }
+
+
+    @Test
+    public void testSave() throws IOException, ConfigurationException
+    {
+        // save the YAMLConfiguration as a String...
+        StringWriter sw = new StringWriter();
+        yamlConfiguration.write(sw);
+        String output = sw.toString();
+
+        // ..and then try parsing it back as using SnakeYAML
+        Map parsed = new Yaml().loadAs(output, Map.class);
+        assertEquals(6, parsed.entrySet().size());
+        assertEquals("value1", parsed.get("key1"));
+
+        Map key2 = (Map) parsed.get("key2");
+        assertEquals("value23", key2.get("key3"));
+
+        List<String> key5 = (List<String>) ((Map) 
parsed.get("key4")).get("key5");
+        assertEquals(2, key5.size());
+        assertEquals("col1", key5.get(0));
+        assertEquals("col2", key5.get(1));
+    }
+
+    @Test
+    public void testGetProperty_dictionary()
+    {
+        assertEquals("Martin D'vloper", 
yamlConfiguration.getProperty("martin.name"));
+        assertEquals("Developer", yamlConfiguration.getProperty("martin.job"));
+        assertEquals("Elite", yamlConfiguration.getProperty("martin.skill"));
+    }
+
+    /**
+     * Removes the test output file if it exists.
+     */
+    private void removeTestFile()
+    {
+        if (testSaveConf.exists())
+        {
+            assertTrue(testSaveConf.delete());
+        }
+    }
+}

Added: commons/proper/configuration/trunk/src/test/resources/test.json
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/resources/test.json?rev=1799501&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/test/resources/test.json (added)
+++ commons/proper/configuration/trunk/src/test/resources/test.json Wed Jun 21 
20:00:25 2017
@@ -0,0 +1,27 @@
+{
+  "key1": "value1",
+  "key2": {
+    "key3": "value23"
+  },
+  "key4": {
+    "key5": [
+      "col1",
+      "col2"
+    ]
+  },
+  "very": {
+    "nested": {
+      "properties": [
+        "nested1",
+        "nested2",
+        "nested3"
+      ]
+    }
+  },
+  "int1": 37,
+  "martin": {
+    "name": "Martin D'vloper",
+    "job": "Developer",
+    "skill": "Elite"
+  }
+}

Added: commons/proper/configuration/trunk/src/test/resources/test.yaml
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/resources/test.yaml?rev=1799501&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/test/resources/test.yaml (added)
+++ commons/proper/configuration/trunk/src/test/resources/test.yaml Wed Jun 21 
20:00:25 2017
@@ -0,0 +1,26 @@
+---
+key1:
+  value1
+
+key2:
+  key3: value23
+
+key4:
+  key5:
+    - col1
+    - col2
+
+very:
+  nested:
+    properties:
+      - nested1
+      - nested2
+      - nested3
+
+int1:
+  37
+
+martin:
+    name: Martin D'vloper
+    job: Developer
+    skill: Elite
\ No newline at end of file


Reply via email to