Reputation: 77115
I have created a Bean Class using Builder Pattern and having issues creating an object from a yaml file.
Here is a sample class (Actual class is quite big, this is just an excerpt incase if you wanted to answer with an example):
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();
}
}
I am using snakeyaml to load the file but any yaml api would work. Since the displayName
is a mandatory param, I want to pass that value while creating the instance. The other params can be passed while creating the object but I would like the option to load them through yaml file.
I am able to load the yaml file if I use java bean. Is there a way to instantiate builder objects?
I tried:
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());
but I get following error:
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"
Upvotes: 4
Views: 2969
Reputation: 11701
SnakeYaml is a very powerful library & it provides support for creating instance based on constructor injection.
/**
* 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 can be viewed here. So your code still holds good. You may need to change yaml content with proper format. Once done, you are all set.
Upvotes: 2
Reputation: 7188
The exception is about there not being a no-arg constructor in Builder
, as you probably figured out.
What you could do is to allow no-arg constructor for Builder
and add corresponding setter and getter for displayName
to it. Then simply throw an exception in build()
if displayName
is not set (or provide a default value for it). The exception can be a runtime one, or you could make it clear and add an explicit throws
.
While it is not the prettiest solution, it should work just fine. The fact that the Builder
is created without a mandatory argument should not matter, as it is the ClientBuilder
that needs to be constructed properly (as the factory/builder is used to ensure that each instance of whatever it is building is correct).
I have no way to access any yaml parsing tools for Java currently, but if there is any way I could improve my answer, let me know - I will be happy to do so.
Upvotes: 1