ilmoeuro / snakeyaml

Automatically exported from code.google.com/p/snakeyaml
0 stars 0 forks source link

Regression: cannot create JavaBean containing a custom element with more than one constructor #11

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
The following test fails for SnakeYAML 1.4-SNAPSHOT, but succeeds for
SnakeYAML 1.3:

It succeeds if you remove the 2nd constructor from class Custom.
Note that the direct creation of the object ("key3") works in 1.4-SNAP too.

import junit.framework.TestCase;

import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.Loader;
import org.yaml.snakeyaml.Dumper;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.representer.Represent;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.constructor.Construct;
import org.yaml.snakeyaml.constructor.ConstructorException;

import java.io.*;
import java.util.*;

/**
** @author infinity0
*/
public class YamlMapTest extends TestCase {

    public void testYamlMap() throws IOException {
        Map<String, Object> data = new TreeMap<String, Object>();
        data.put("key3", new Custom("test"));
        data.put("key4", new Wrapper("test", new Custom("test")));

        Yaml yaml = new Yaml(new Loader(new ExtendedConstructor()),
                            new Dumper(new ExtendedRepresenter(), new DumperOptions()));
        File file = new File("beantest.yml");

        FileOutputStream os = new FileOutputStream(file);
        yaml.dump(data, new OutputStreamWriter(os));
        os.close();

        FileInputStream is = new FileInputStream(file);
        Object o = yaml.load(new InputStreamReader(is));
        is.close();

        assertTrue(o instanceof Map);
        Map m = (Map)o;
        assertTrue(m.get("key3") instanceof Custom);
        assertTrue(m.get("key4") instanceof Wrapper);
    }

    public static class Wrapper {
        private String a;
        private Custom b;
        public Wrapper(String s, Custom bb) { a = s; b = bb; }
        public Wrapper() { }
        public String getA() { return a; }
        public void setA(String s) { a = s; }
        public Custom getB() { return b; }
        public void setB(Custom bb) { b = bb; }
    }

    public static class Custom {
        final private String str;
        public Custom(String s) {
            str = s;
        }
        public Custom(Integer i) {
            str = "";
        }
        public Custom(Custom c) {
            str = c.str;
        }
        public String toString() { return str; }
    }

    public static class ExtendedRepresenter extends Representer {
        public ExtendedRepresenter() {
            this.representers.put(Custom.class, new RepresentCustom());
        }

        private class RepresentCustom implements Represent {
            @Override public Node representData(Object data) {
                return representScalar("!Custom", ((Custom) data).toString());
            }
        }
    }

    public static class ExtendedConstructor extends Constructor {
        public ExtendedConstructor() {
            this.yamlConstructors.put("!Custom", new ConstructCustom());
        }

        private class ConstructCustom implements Construct {
            @Override public Object construct(Node node) {
                String str = (String) constructScalar((ScalarNode)node);
                return new Custom(str);
            }
            @Override public void construct2ndStep(Node node, Object object) { }
        }
    }

}

error with 869:49ff0bdb91f8

    [junit] Testcase: testYamlMap took 0.097 sec
    [junit]     Caused an ERROR
    [junit] null; Can't construct a java object for
tag:yaml.org,2002:plugins.Library.serial.YamlMapTest$Wrapper;
exception=Cannot create property=b for
JavaBean=plugins.Library.serial.YamlMapTest$Wrapper@442a15cd; More then 1
constructor with 1 argument found for class
plugins.Library.serial.YamlMapTest$Custom
    [junit] Can't construct a java object for
tag:yaml.org,2002:plugins.Library.serial.YamlMapTest$Wrapper;
exception=Cannot create property=b for
JavaBean=plugins.Library.serial.YamlMapTest$Wrapper@442a15cd; More then 1
constructor with 1 argument found for class
plugins.Library.serial.YamlMapTest$Custom
    [junit]  in "<reader>", line 1, column 6
    [junit] 
    [junit]     at
org.yaml.snakeyaml.constructor.Constructor$ConstructYamlObject.construct(Constru
ctor.java:283)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.callConstructor(BaseConstructor.j
ava:172)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.j
ava:155)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConst
ructor.java:257)
    [junit]     at
org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConst
ructor.java:112)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.
java:238)
    [junit]     at
org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeCo
nstructor.java:433)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.callConstructor(BaseConstructor.j
ava:172)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.j
ava:155)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.constructDocument(BaseConstructor
.java:114)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.getSingleData(BaseConstructor.jav
a:100)
    [junit]     at org.yaml.snakeyaml.Loader.load(Loader.java:39)
    [junit]     at org.yaml.snakeyaml.Yaml.load(Yaml.java:164)
    [junit]     at
plugins.Library.serial.YamlMapTest.testYamlMap(YamlMapTest.java:47)
    [junit] Caused by: org.yaml.snakeyaml.error.YAMLException: Cannot
create property=b for
JavaBean=plugins.Library.serial.YamlMapTest$Wrapper@442a15cd; More then 1
constructor with 1 argument found for class
plugins.Library.serial.YamlMapTest$Custom
    [junit]     at
org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.constructJavaBean2nd
Step(Constructor.java:227)
    [junit]     at
org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.construct(Constructo
r.java:138)
    [junit]     at
org.yaml.snakeyaml.constructor.Constructor$ConstructYamlObject.construct(Constru
ctor.java:281)
    [junit] Caused by: org.yaml.snakeyaml.error.YAMLException: More then 1
constructor with 1 argument found for class
plugins.Library.serial.YamlMapTest$Custom
    [junit]     at
org.yaml.snakeyaml.constructor.Constructor$ConstructScalar.construct(Constructor
.java:318)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.callConstructor(BaseConstructor.j
ava:172)
    [junit]     at
org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.j
ava:155)
    [junit]     at
org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.constructJavaBean2nd
Step(Constructor.java:220)
    [junit] 

Original issue reported on code.google.com by infinity0x@gmail.com on 5 Aug 2009 at 9:11

GoogleCodeExporter commented 9 years ago
In case of a few constructors with 1 argument it requires more "artificial
intelligence" to pick up the proper one.

N.B. Instead of "ConstructCustom implements Construct" you can use 
"ConstructCustom
extends AbstractConstruct". Then you do not have to implement 
"construct2ndStep(Node
node, Object object)". (Anyway the ConstructCustom implementation does not 
support
recursive structures)

Original comment by aso...@gmail.com on 5 Aug 2009 at 12:24

GoogleCodeExporter commented 9 years ago
in case there is more then 1 constructor with 1 argument to create an instance 
for
scalar node and a global or local tag does not allow to rely on the implicit 
type
then try to use the constructor with the String argument

Original comment by aso...@gmail.com on 5 Aug 2009 at 12:59

GoogleCodeExporter commented 9 years ago
but why does it work fine when the object is outside of a JavaBean? also, it 
seems to
pick the correct constructor in version 1.3 and before..

Original comment by infinity0x@gmail.com on 5 Aug 2009 at 1:54

GoogleCodeExporter commented 9 years ago
You are right. Inside the JavaBean the custom constructor is not respected. I 
will
try to improve it. 

Original comment by aso...@gmail.com on 5 Aug 2009 at 8:45

GoogleCodeExporter commented 9 years ago
Fixed.
Now I create a Java instance with the following priority to choose the class:
Explicit tag -> Runtime class (defined in JavaBean) -> implicit tag

A custom constructor is always respected.

Please check YamlMapTest.

Original comment by py4fun@gmail.com on 6 Aug 2009 at 12:41

GoogleCodeExporter commented 9 years ago
yup, this is now fixed. thank you :)

Original comment by infinity0x@gmail.com on 6 Aug 2009 at 2:54

GoogleCodeExporter commented 9 years ago

Original comment by aso...@gmail.com on 6 Aug 2009 at 9:58

GoogleCodeExporter commented 9 years ago

Original comment by aso...@gmail.com on 6 Aug 2009 at 10:03