从Yaml文件加载Java Builder对象

从Yaml文件加载Java Builder对象,java,yaml,builder-pattern,Java,Yaml,Builder Pattern,我使用构建器模式创建了一个Bean类,但在从yaml文件创建对象时遇到了问题 下面是一个示例类(实际类非常大,如果您想用一个示例来回答,这只是一个摘录): 我正在使用snakeyaml加载文件,但任何yaml api都可以工作。由于displayName是一个强制参数,因此我希望在创建实例时传递该值。其他参数可以在创建对象时传递,但我希望通过yaml文件加载它们 如果我使用JavaBean,我可以加载yaml文件。有没有实例化生成器对象的方法 我试过: InputStream input = n

我使用构建器模式创建了一个Bean类,但在从yaml文件创建对象时遇到了问题

下面是一个示例类(实际类非常大,如果您想用一个示例来回答,这只是一个摘录):

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

如果我使用JavaBean,我可以加载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"
线程“main”中的异常无法为标记构建java对象:yaml.org,2002:com.xxx.xxx.xxx.ClientBuilder$Builder;exception=java.lang.NoSuchMethodException:com.xxx.xxx.xxx.ClientBuilder$Builder。() 在“阅读器”第2行第1列: 名字:“杰帕尔”
例外情况是,
Builder
中没有无参数构造函数,您可能已经明白了这一点

您可以做的是不允许
Builder
使用arg构造函数,并为
displayName
添加相应的setter和getter。如果未设置
displayName
(或为其提供默认值),则只需在
build()
中抛出异常即可。异常可以是运行时异常,也可以明确并添加显式的
throws

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


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

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

/**
 * 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());
}
可以在这里查看。
因此,您的代码仍然有效。您可能需要使用适当的格式更改yaml内容。一旦完成,您就万事俱备。

谢谢您留下的答案。是的,添加no-arg构造函数是有效的,但只有当我将
Builder
类的所有
setter
设置为
void
而不是
Builder
返回类型时,它才有效。如果我失去了流畅界面的能力,那么我想我应该使用bean模式。您的想法是什么?您可能不应该太拘泥于模式,它们应该是更多的指导原则,而不是严格的代码;)没有什么能阻止你保持二传手的状态,至少我没有看到。是的,存在
Builder
处于不正确状态(no
displayName
set)的风险,但即使设置程序返回
void
,也存在这种风险。这里的诀窍是延迟错误状态真正重要的时刻。在构造
Builder
或者更确切地说是
ClientBuilder
时,它是否重要?在这种情况下,后者似乎更合适,至少对我来说是这样。我必须说一些明智的话<代码>:)。设定者不符合bean标准,而snakeyaml似乎希望如此。如果我把它改回我的旧setters(返回这个,而不是void),它就会出错。这将是测试套件的一部分,在最近阅读了有效的Java之后,我认为构建器模式更合适。我也喜欢吃豆子。它们很容易创建(感谢eclipse自动代码生成
:P
)!
/**
 * 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());
}