Convert String to byte[] when it matches the JavaBean property

Issue #343 wontfix
Alex Barcelo created an issue

This is the counterpart of #302, if I understood correctly.

Currently, if a JavaBean contains a byte[] field and the Yaml contains a regular string for that field, the load will fail with: No single argument constructor found for class [B

It would be expected behaviour (?) to automatically store that as a byte array.

In particular, Python 2.7 pyyaml implementation does not give an explicit (simple) way to indicate whether a certain field is !!str or !!binary:

str objects are converted into !!str, !!python/str or !binary nodes depending on whether the object is an ASCII, UTF-8 or binary string.

[Taken from http://pyyaml.org/wiki/PyYAMLDocumentation]

To replicate create a simple JavaBean:

class C {
    private byte[] code;

    public byte[] getCode() {
        return this.code;
    }

    public void setCode(final byte[] newcode) {
        this.code = newcode;
    }
}

And it seems to fail with

!!c
code: "this is code"

Comments (8)

  1. Andrey Somov

    Alex, the error message that you show looks unrelated to the problem you describe. It looks like you want to create an instance of C with a single argument (which is a byte array). But the C class definition has no constructor (which means that it has only empty constructor). Try to write a test and attach it here. Or try to add

    public C(byte[] c) {
        this.code = c;
    }
    
  2. Alex Barcelo reporter

    I was trying to make a MCVE, as I had a more complex scenario with JavaBeans. The problem seemed to disappeared when I changed the type from byte[] to String, so I assumed...

    Sorry for the incompleteness, I will double check the problem and the example.

  3. Alex Barcelo reporter

    Consider the following class

    public class C {
        private String name;
        private byte[] data;
    
        public void setName(String newname) {
            name = newname;
        }
    
        public String getName() {
            return name;
        }
    
        public void setData(byte[] newdata) {
            data = newdata;
        }
    
        public byte[] getData() {
            return data;
        }
    }
    

    Which I believe is a valid JavaBean. At least behaves well as such.

    Then consider the trivial valid approaches:

    Yaml yaml = new Yaml();
    
    C o = new C();
    o.setData(new byte[]{0, 1, 2, 3});
    o.setName("MyName");
    String dumpedC = yaml.dump(o);
    System.out.print(dumpedC);
    
    dumpedC = "!!yamltest.C\n"
            + "data: !!binary |-\n"
            + "  AAECAw==\n"
            + "name: MyName";
    
    o = (C)yaml.load(dumpedC);
    System.out.println(o.getName());
    System.out.println(o.getData());
    

    Everything works as expected. However, if instead of an explicit !!binary we leave a default string:

    dumpedC = "!!yamltest.C\n"
            + "data: \"binarydataasstring\"\n"
            + "name: MyName";
    
    o = (C)yaml.load(dumpedC);
    

    That crashes. And the message is something along the lines:

    Cannot create property for ...
    No single argument constructor found for class [B
    

    Having the automatic conversion from a YAML !!str implicit field into a byte[] would be natural (as I see it), and is specially cumbersome in my scenario where the Python counterpart (serialization) is done through pyyaml (using Python 2.7) which converts to !!str or !!bytes without (apparent) way to override.

  4. Andrey Somov

    I got the point. It should probably be explained on our wiki.

    (Python 2 has a well recognised problem: it thinks that string and byte array are the same. Python 3 solves the issue. Java does not have this problem.)

    1. Can you change C class ? (to add another setter with String instead of bytes)
    2. You need to write a custom Constructor and explicitly convert String to bytes.
    3. Constructor.constructJavaBean2ndStep:306-308 can also be used as an example (of the opposite step: bytes -> String)
  5. Alex Barcelo reporter

    Yes, the different behaviour and explicit types between Python 2 and Python 3 are a real nuissance, in this cases.

    For what I am currently working on, your first option would be the less intrusive option, I guess. I will soon be able to change the Java code (I am in charge of the Python backend, so I was trying to minimize changes to Java, but doing so is feasible).

    I was looking for expected behaviour, found the #302 (which I understand is the reciprocal) and decided to post this issue. I could not find further information about my reported specific use case.

    Thanks!

  6. Andrey Somov

    The issue #302 is the other way around. (I mentioned it as p.3)

    I think now it was a mistake to do #302. SnakeYAML converts byte[] to String without taking encoding into account (The Python 2 way). Encoding must be explicit.

  7. Log in to comment