从Yaml文件加载Java Builder对象

时间:2015-03-22 06:46:33

标签: java yaml builder-pattern

我使用Builder Pattern创建了一个Bean类,并且在从yaml文件创建对象时遇到了问题。

这是一个示例类(实际类非常大,如果你想回答一个例子,这只是一个摘录):

public class ClientBuilder {

    private final String firstName;
    private final String lastName;
    private final String displayName;

    private ClientBuilder(Builder builder) { 
        firstName   = builder.firstName; 
        lastName    = builder.lastName; 
        displayName = builder.displayName;
    }

    public static class Builder {

        private final String displayName; // Mandatory Attribute

        public Builder( String displayName ) {
            this.displayName = displayName;
        }

        private String firstName;
        private String lastName;

        public Builder setFirstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public Builder setLastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public ClientBuilder build() {
            return new ClientBuilder(this);
        }
    }

    @Override
    public String toString() {
        StringBuffer sbf = new StringBuffer();
        sbf.append("New Company Object: \n");
        sbf.append("firstName   : " +  this.firstName   + "\n");
        sbf.append("lastName    : " +  this.lastName    + "\n");
        sbf.append("displayName : " +  this.displayName + "\n");
        return sbf.toString();
    }
}

我使用snakeyaml加载文件,但任何yaml api都可以。由于displayName是强制参数,我想在创建实例时传递该值。其他参数可以在创建对象时传递,但我希望通过yaml文件加载它们。

如果我使用java bean,我可以加载yaml文件。有没有办法实例化构建器对象?

我试过了:

InputStream input = new FileInputStream(new File("src/main/resources/client.yaml"));
Yaml yaml = new Yaml();
Builder builder = new Builder("Display Name");
builder = (Builder) yaml.loadAs(input, ClientBuilder.Builder.class);
ClientBuilder client = builder.build();
System.out.println(client.toString());

但我收到以下错误:

Exception in thread "main" Can't construct a java object for tag:yaml.org,2002:com.xxx.xxx.xxx.ClientBuilder$Builder; exception=java.lang.NoSuchMethodException: com.xxx.xxx.xxx.ClientBuilder$Builder.<init>()
 in 'reader', line 2, column 1:
    firstName: "Jaypal"

2 个答案:

答案 0 :(得分:2)

SnakeYaml是一个非常强大的库和&amp;它支持基于构造函数注入创建实例。

/**
 * create JavaBean
 */
public void testGetBeanAssumeClass() {
    String data = "--- !!org.yaml.snakeyaml.constructor.Person\nfirstName: Andrey\nage: 99";
    Object obj = construct(data);
    assertNotNull(obj);
    assertTrue("Unexpected: " + obj.getClass().toString(), obj instanceof Person);
    Person person = (Person) obj;
    assertEquals("Andrey", person.getFirstName());
    assertNull(person.getLastName());
    assertEquals(99, person.getAge().intValue());
}

/**
 * create instance using constructor arguments
 */
public void testGetConstructorBean() {
    String data = "--- !!org.yaml.snakeyaml.constructor.Person [ Andrey, Somov, 99 ]";
    Object obj = construct(data);
    assertNotNull(obj);
    assertTrue(obj.getClass().toString(), obj instanceof Person);
    Person person = (Person) obj;
    assertEquals("Andrey", person.getFirstName());
    assertEquals("Somov", person.getLastName());
    assertEquals(99, person.getAge().intValue());
}

Junit code sample可在此处查看。 所以你的代码仍然很好。您可能需要使用正确的格式更改yaml内容。一旦完成,你就完成了。

答案 1 :(得分:1)

例外情况是Builder中没有no-arg构造函数,正如您可能想到的那样。

你可以做的是为Builder允许no-arg构造函数,并为displayName添加相应的setter和getter。如果未设置build()(或为其提供默认值),则只需在displayName中抛出异常。例外可以是运行时例外,或者您可以将其清除并添加明确的throws

虽然它不是最漂亮的解决方案,但应该可以正常工作。在没有强制参数的情况下创建Builder这一事实无关紧要,因为需要正确构造ClientBuilder(因为工厂/构建器用于确保每个实例都是如此)正在建设是正确的。)

我目前无法访问任何针对Java的yaml解析工具,但如果有任何方法可以改进我的答案,请告诉我 - 我很乐意这样做。