5

I'm trying to serialize a Java instance having fields like follows.

public class Person{
    private String firstName;
    private String lastName;

    public String getFirstName() {

        return firstName;
    }

    public void setFirstName(String firstName) {

        this.firstName = firstName;
    }

    public String getLastName() {

        return lastName;
    }

    public void setLastName(String lastName) {

        this.lastName = lastName;
    }
}

How do I serialize them in different names than the actual field names? In Gson this is possible by using @SerializedName("first-name") annotation as follows.

@SerializedName("first-name")
private String firstName;

Is there something similar to above in snakeyaml. The dependency details for snakeyaml is as follows,

        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.17</version>
        </dependency>

Below is the main class of the program with the answer provided by flyx

public class Demo {

    public static void main(String[] args) {

        Person person = new Person();
        person.setFirstName("Kasun");
        person.setLastName("Siyambalapitiya");

        Constructor constructor = new Constructor(Person.class);
        Representer representer = new Representer();
        TypeDescription personDesc = new TypeDescription(Person.class);
        personDesc.substituteProperty("first-name", Person.class, "getFirstName", "setFirstName");
        personDesc.substituteProperty("last-name", Person.class, "getLastName", "setLastName");
        constructor.addTypeDescription(personDesc);
        representer.addTypeDescription(personDesc);
        Yaml yaml = new Yaml(constructor, representer);
        String yamlString = yaml.dumpAs(person, Tag.MAP, DumperOptions.FlowStyle.BLOCK);

        Path updateDescriptorFilePath =
                Paths.get(File.separator + "tmp" + File.separator + "sample.json");
        try {
            FileUtils.writeStringToFile(updateDescriptorFilePath.toFile(), yamlString);
        } catch (IOException e) {
            System.out.println(e.getCause());
        }
    }
}

The above results in the following output

/tmp  cat sample.json
first-name: Kasun
last-name: Siyambalapitiya
firstName: Kasun
lastName: Siyambalapitiya
 /tmp   

Kasun Siyambalapitiya
  • 3,956
  • 8
  • 38
  • 58
  • 1
    Jackson can serialize to YAML (in fact, it uses SnakeYAML behind the scenes) and you can use the `@JsonProperty` annotation to do this. See https://stackoverflow.com/questions/35217410/how-to-parse-part-of-a-yaml-file-in-snakeyaml/37356162 – Michael Jan 24 '19 at 17:04

2 Answers2

4
Constructor constructor = new Constructor(Person.class);
Representer representer = new Representer();
TypeDescription personDesc = new TypeDescription(Person.class);
personDesc.substituteProperty("first-name", String.class,
        "getFirstName", "setFirstName");
constructor.addTypeDescription(personDesc);
representer.addTypeDescription(personDesc);
Yaml yaml = new Yaml(constructor, representer);
// and then load /dump your file with it
JD Frias
  • 4,418
  • 3
  • 21
  • 24
flyx
  • 35,506
  • 7
  • 89
  • 126
  • You need to describe *what* is not working and show your code (edit the question). – flyx Jan 28 '19 at 13:59
  • I have added the code I have tried out, please have a look – Kasun Siyambalapitiya Jan 28 '19 at 14:37
  • Sorry, I forgot that you need to define a `Representer` for dumping. Try again. – flyx Jan 28 '19 at 14:44
  • now the output has both values as follows, ` cat sample.json first-name: Kasun last-name: Siyambalapitiya firstName: Kasun lastName: Siyambalapitiya ` – Kasun Siyambalapitiya Jan 28 '19 at 15:01
  • I have updated the code and the output, please have a look – Kasun Siyambalapitiya Jan 28 '19 at 15:05
  • 1
    Hmm. This actually seems to be a limitation of SnakeYAML, see [this issue](https://bitbucket.org/asomov/snakeyaml/issues/403/typedescriptionsubstituteproperty-doesnt). I cannot think of a trivial solution; you probably would need to subclass `TypeDescription` for a workaround :( – flyx Jan 28 '19 at 15:24
  • @Flyx how do you do this for classes nested within the root class? – DaBlick Aug 12 '21 at 20:42
  • @DaBlick You can add type descriptions for any class, not just the root class. The code is the same, just keep the root class as constructor argument and add type descriptions for the relevant nested classes. – flyx Aug 12 '21 at 23:45
1

For those running into this problem I solved it by using both a substitution and exclusion:

TypeDescription personDesc = new TypeDescription(Person.class);
personDesc.substituteProperty("first-name", Person.class, "getFirstName", "setFirstName");
personDesc.substituteProperty("last-name", Person.class, "getLastName", "setLastName");
personDesc.setExcludes("firstName", "lastName");

This worked for me - at least for dumping the YAML.

user1995422
  • 220
  • 1
  • 3
  • 13
  • It should be `personDesc.substituteProperty("first-name", String.class, "getFirstName", "setFirstName");` As String is the property type see: https://www.javadoc.io/doc/org.yaml/snakeyaml/1.19/org/yaml/snakeyaml/TypeDescription.html#substituteProperty-java.lang.String-java.lang.Class-java.lang.String-java.lang.String-java.lang.Class...- – Harry Su Aug 05 '22 at 00:32