I am using Jackson's XML binding to convert XML into a Java List, via an initial mapping to a Java POJO. What I have written works but I don't think I am using Jackson correctly.
I am stuck with this ugly XML:
<groups>
<groups>One</groups>
<groups>Two</groups>
<groups>Three</groups>
</groups>
Here is the Java POJO I am using. Note the setGroups(String) method is actually adding to the list.
public class Groups {
private List<String> groups = new ArrayList<String>();
public void setGroups(String group) {
groups.add(group);
}
public List<String> getGroups() {
return this.groups;
}
}
Here is how I invoke Jackson's XmlMapper.
public List<String> getListOfGroups(String xmlDoc) {
XmlMapper mapper = new XmlMapper();
Groups groups = mapper.readValue(xmlDoc, Groups.class);
return groups.getGroups();
}
This is actually working as I need it to work. I get a Groups class with a list populated with the elements I expect. I am wondering, is approach is correct? I don't like that I have a setter doing an add but everything I've tried has not worked.
Your POJO could be simple like this:
public class Groups {
private List<String> groups;
public List<String> getGroups() {
return this.groups;
}
}
It will work fine since you use the MapperFeature.USE_GETTERS_AS_SETTERS (enabled by default).
Related
I am trying to split String values based on de-limiter and trim them before putting them in a list.
I am able to split the values, could you please suggest how can be trim the list.
#Value("#{'${string-values}'.split(',')}")
private List<String> textList;
The problem it seems is, Split returns a list and I need to invoke trim() before storing them in the variable.
Check
Java - Split and trim in one shot
#Value("#{'${string-values}'.split('\\s*,\\s*')}")
private List<String> textList;
Better to provide no space between values in properties file.
To put a check in code it can be done in this way.
private List<String> textList;
public YourController(#Value("${string-values}") String propertyFromFile) {
this.textList = new ArrayList<>();
Arrays.asList(propertyFromFile.split(",")).forEach(b-> this.textList.add(b.trim()));
}
I think it may be better to use #Configuration and then process that instead of doing like this, however you can add a new annotation on top of value annotation and use that annotation to process the list. For example
#Target(value = {ElementType.TYPE})
#Value
public #interface Trim {
//Override the method you want to override
}
Public TrimPricessor {
//Implement the annotation method here
}
Since you are using Spring Boot, use:
#Component
#ConfigurationProperties(prefix = "foo.bar")
public class MyConfig {
private List<String> list = new ArrayList<>();
public List<String> getList() {
return this.list;
}
public void setList(List<String> list) {
this.list = list;
}
}
foo.bar.list= a , b, c
The list entries are trimmed.
For this JSON:
{"key.a": "a", "key.b": "b"}
I want to load values "a" and "b" to List using #JsonProperty and Jackson. I tried:
#JsonProperty("key.*")
List<String> values;
But it doesn't work. Any thoughts?
You can use the JsonAnySetter annotation, which will map any unknown Json property, on a setter method:
private List<String> values = new ArrayList<>();
#JsonAnySetter
public void setValues(String key, String value) {
// You can perform a pattern validation on the key if wanted
this.values.add(value);
}
public List<String> getValues() {
return this.values;
}
An alternative solution would be to implement a custom JsonSerializer & JsonDeSerializer as well.
For example here is my code :
List<EmployeeDto> employees = new ArrayList<>();
if(CollectionUtils.isNotEmpty(employeeListFromUI)){
for(EmployeeVO employee : employeeListFromUI){
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.setName(employee.getName());
employeeDto.setAddressLine1(employee.getAddressLine1());
if(employee.getAddressLine2()!=null && employee.getAddressLine2()!=""){
employeeDto.setAddressLine2(employee.getAddressLine2());
}
employeeDto.setCity(employee.getCity());
employeeDto.setState(employee.getState());
employeeDto.setZip(employee.getZip());
employees.add(employeeDto);
}
}
instead of this much of code using java 8 stream and forEach is it possible to write the above code in few lines.
You could move this:
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.setName(employee.getName());
employeeDto.setAddressLine1(employee.getAddressLine1());
if(employee.getAddressLine2()!=null && employee.getAddressLine2()!=""){
employeeDto.setAddressLine2(employee.getAddressLine2());
}
employeeDto.setCity(employee.getCity());
employeeDto.setState(employee.getState());
employeeDto.setZip(employee.getZip());
employees.add(employeeDto);
to a separate method, like :
private static EmployeeDto transform(EmployeeVO input) {
// transform and return
}
And the use it in the form:
employeeListFromUI.stream()
.map(YourClass::tranform)
.collect(Collectors.toList());
In order to avoid this boiler plate code, you can use Dozer mapping API for transforming your EmployeeVO into EmployeeDto with very minimal code. You can look here for Dozer example.
So, by with Dozer mapping, your code looks like:
EmployeeDtoList class:
public class EmployeeDtoList {
private List<EmployeeDto> employees;
//add getters and setters
}
EmployeeVOList class:
public class EmployeeVOList {
private List<EmployeeVO> employees;
//add getters and setters
}
Converting beans:
Mapper mapper = new DozerBeanMapper();
EmployeeVOList employeeVOList =
mapper.map(employeeListFromUI, EmployeeVOList.class);
You can use either dozer annotations (i.e., uses #Mapping annotation, look here) or xml to map each field inside EmployeeDto to EmployeeVO
Note: Dozer API
just avoids the boilerplae code i.e., it does NOT specifically avoid any iterations to copy the bean properties from one object into another.
Optional + flatMap + map:
Optional.ofNullable(employeeListFromUI)
.flatMap(employeeList-> employeeList
.stream.map(e -> mapToEmployeeDto(e))).collect(Collectors.toList());
Example
I'm using JAXB to convert java object into xml file.
In my XML file, I need to remove the tag without using XSLT .
For example :remove the tag orders
<order_List>
<orders>
<orderid>12324<orderid>
</orders>
</order_List>
Excepted result :
<order_List>
<orderid>12324<orderid>
</order_List>
I can suggest you "naive" approach.
The wrapper tag orders can be configured using JAXB annotation #XmlElementWrapper. So, you can create 2 models: one that contain this tag, another that does not. You can use the model that contains this tag to parse you data, then copy data to model that does not contain this tag and then use it to serialize.
#XmlRootElement(name = "index-annotations")
public class OrderList {
private Collection<Integer> orderIds;
#XmlElement(name = "orderid", type = Integer.class)
public Collection<Integer> getOrderId() {
return orderIds;
}
}
#XmlRootElement(name = "index-annotations")
public class OutputOrderList extends OrderList {
#Override
#XmlElement(name = "orderid", type = Integer.class)
#XmlElementWrapper(name="orders")
public Collection<Integer> getOrderId() {
return orderIds;
}
}
Obviously this solution contains a kind of duplicate code, however it is probably better then configuring 2 schemas using XML because of compile time validation of annotations validity.
I am using Play Framework 1.2.4 with Java and using JPA to persist my database objects. I have several Model classes to be rendered as JSON. But the problem is I would like to customize these JSON responses and simplify the objects just before rendering as JSON.
For instance, assume that I have an object named ComplexClass and having properties id, name, property1,...,propertyN. In JSON response I would like to render only id and name fields.
What is the most elegant way of doing this? Writing custom binder objects or is there simple JSON mapping such as using a template?
Play Framework 1.2.4 directly depends on the gson library so you could use that to render your JSON strings. All you have to do is use gson's #Expose annotation. So in your example, you would mark the fields you want in your JSON string like this:
public class ComplexClass {
#Expose
public Long id;
#Expose
public String name;
...
}
Then in your controller, you would just do this:
public static void someActionMethod() {
// get an instance of your ComplexClass here
ComplexClass complex = ...
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
String json = gson.toJson(complex);
renderJson(json);
}
See documentation here.
If ComplexClass is actually a play.db.jpa.Model and therefore the id field is abstracted away in a parent class and you can't put the #Expose annotation on it, then you could create your own ExclusionStrategy that skips fields that aren't annotated with #Expose and are not called id. So something like this (pseudo-code):
public final class ComplexClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes attributes) {
if (name of field is "id") return false;
if (field is annotated with #Expose) return false;
return true;
}
Then the controller would altered slightly to look like this:
GsonBuilder builder = new GsonBuilder();
ComplexClassExclusionStrategy strategy = new ComplexClassExclusionStrategy();
builder.setExclusionStrategies(strategy);
Gson gson = builder.create();
String json = gson.toJson(complex);
renderJson(json);
Use FlexJSON, it's really easy. It allows you to create JSONSerializers which can include/exclude the fields you want.
Check out this article for some examples of using it with Play! Framework.
Here's a simple example:
public ComplexClass {
public Long id;
public String name;
// And lots of other fields you don't want
public String toJsonString() {
// Include id & name, exclude all others.
JSONSerializer ser = new JSONSerializer().include(
"id",
"name",
).exclude("*");
return ser.serialize(this);
}
}
You can add it to your dependencies.yml like so:
require:
- play
- net.sf.flexjson -> flexjson 2.1
What I usually do is write an interface for models that implements a toJSONString() method so that I can call renderJSON(someModel.toJSONString()) in the controller.
Link to official website
EDIT: Extra example for lists/collections
Ok, when you start serializing list you might get some unexpected results. This is because the order of evaluation is important. The first include() or exclude() takes precedence over the following ones.
Here's an example of serializing the childs of a parent entity (OneToMany relation).
JSONSerializer ser = new JSONSerializer();
// Exclude these standard fields from childs
ser.exclude(
"*.persistent",
"*.class",
"*.entityId"
);
// Include childs and all its other fields
ser.include(
"childs",
"childs.*"
);
// Exclude everything else
ser.exclude("*");
String data = ser.serialize(parent);
The * is a wildcard by the way. This piece of documentation explains it perfectly:
An exclude of *.class will match to any path depth. So if flexjson is serializing the field with path of "foo.bar.class" the * in *.class will match foo.bar.