javajacksonyamlsnakeyaml

How to parse snake_case properties in SnakeYAML


Jackson provides a dead simple way to parse camel_case properties. For example:

jackson.yaml with snake_case properties:

customers:
  - first_name: "John"
    last_name: "Doe"
    age: 52

Code for parse:

ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("jackson.yaml");
Orders orders = objectMapper.readValue(inputStream, Orders.class);

But is there something similar in SnakeYaml (such as PropertyNamingStrategies.SNAKE_CASE option) ?

PS For now, the only way I've found is to use the @YamlProperty annotation on each property separately


Solution

  • As far as I know SnakeYAML uses reflection inside, so because of that you need to have property names in your classes equal to property name in your YAML file.

    So in your case if you have yaml file with snake case you would have to use property name in your classes with snake case.

    See example below:

    Customer.java

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Customer {
        private String first_name;
        private String last_name;
        private Integer age;
    }
    

    Order.java

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Order {
        private List<Customer> customers;
    }
    

    Main.java

    public class Main {
    
        @SneakyThrows
        public static void main(String[] args) {
            InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("jackson.yaml");
    
            Yaml yaml = new Yaml();
            Order order = yaml.loadAs(inputStream, Order.class);
            System.out.println(order);
        }
    }
    

    jackson.yaml

    customers:
      - first_name: "John"
        last_name: "Doe"
        age: 52
    

    Output:

    Order(customers=[Customer(first_name=John, last_name=Doe, age=52)])
    

    EDIT:

    There is also an option to define TypeDescriptors which tells SnakeYAML how to map fields. See example below:

    public class Main {
    
        @SneakyThrows
        public static void main(String[] args) {
            InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("jackson.yaml");
    
            TypeDescription customerDescriptor = new TypeDescription(Customer.class);
            customerDescriptor.substituteProperty("first_name", String.class, "getFirstName", "setFirstName");
            customerDescriptor.substituteProperty("last_name", String.class, "getLastName", "setLastName");
            customerDescriptor.setExcludes("firstName", "lastName");
    
            Constructor constructor = new Constructor(Customer.class);
            Representer representer = new Representer(new DumperOptions());
    
            constructor.addTypeDescription(customerDescriptor);
            representer.addTypeDescription(customerDescriptor);
    
            Yaml yaml = new Yaml(constructor, representer);
            Order order = yaml.loadAs(inputStream, Order.class);
            System.out.println(order);
        }
    }
    

    jackson.yaml

    customers:
      - first_name: "John"
        last_name: "Doe"
        age: 52
    

    Customer.java

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Customer {
        private String firstName;
        private String lastName;
        private Integer age;
    }
    

    Output:

    Order(customers=[Customer(firstName=John, lastName=Doe, age=52)])