MapStruct map parent field to child array of objects - java

class Item {
String oneTo
}
class Header {
String twoTo;
List <Item> items;
}
class HeaderFrom {
String oneFrom;
String twoFrom;
}
In the above example, I need to map the following scenarios using MapStruct. What is the best way to get this done?
oneFrom -> all the oneTo fields in the list
twoFrom -> twoTo
Thank you.

Can you try
#Mapping(source = "oneFrom", target = "items", qualifiedByName = "oneFromToList")
Header headerFromToHeader(HeaderFrom headerFrom);
In the same mapper class add a method
#Named("oneFromToList")
public static String oneFromToList(List<> input) {
// logic to map Item.oneTo = HeaderFrom.oneFrom
}

Related

Java stream collect as a map using streamed element as key and collected list as value?

so I have seen some similar questions to this but have been unable to apply their answers to my code and am wondering how to do this. So basically I have the following code (simplified):
public Map<String, List<String>> myFunction(String myApp) {
List<String> myIds = myService.getIds(myApp);
return myIds.stream()
.map(id -> {
final List<String> myObjects = myService.getListForId(id);
return new MyWrapper(id, myObjects);
}).collect(Collectors.toMap(a -> a.ID, a -> a.OBJECTS);
}
class MyWrapper {
public final String ID;
public final List<String> OBJECTS;
public MyWrapper(String id, List<String> objects) {
ID = id;
OBJECTS = objects;
}
}
I can't figure out how to access id in the Collectors function since the list has no gettable relation to the id so I created a wrapper class to store both values. Any ideas on a way to do this without the wrapper? Thanks
streams the ids via myService.getIds
then simply maps those ids to the list returned by the service (using a method reference).
Map<String, List<String>> map = myService.getIds(myApp).
stream().collect(Collectors.toMap(id->id, myService::getListForId));

Java Iterate internal information in value of a Map <Key>

Please I'm doing a practice with MongoDB and Spring but I have the following dilemma; in the DB I have the following information:
[
{
"menu":"querys",
"optionsList":[
{
"options":[
"0001",
"0022",
"0014",
"0041",
"0042",
"0043"
]
}
]
},{..},{...}
]
The structure of the object in Java is as follows:
#Document(collection = "menu")
public class GetAllRules implements Serializable {
private static final long serialVersionUID = 7375796948388333325L;
#JsonProperty(value = "menu")
private String name;
#JsonProperty(value = "optionsList")
private List<Map<String, ?>> optionsList;
//Getters and Setters.
With the following method I get the JSON (I'm using the MongoRepository), with the first FOR I get all the information and with the second I get the inner map, but I don't know how to iterate the information found inside options, I would appreciate it if someone can you help me with the issue:
#GetMapping("/final")
public String obtener() {
List<GetAllRules> allRules = iGetMenuService.getAll(); // Mongo List
String key= "options";
for (GetAllRules rules : allRules) {
for (Map<String, ?> internal : rules.getOptionsList()) {
System.out.println(internal.get(key));
}
}
return "finalizado";
}
With line System.out.println(internal.get(key)); I get the key values ​​that I need but now I don't know how to go through it one by one to do something specific with each data.
[0001, 0022, 0014, 0041, 0042, 0043]
[0238]
[1001, 1003]
[0108, 0109, 0102]
[0601, 0602, 0604, 0604]
[0603, 0901, 0901]
[0238]
[0001]
Thanks.
how to iterate the information found inside options
Your options field is just another array/list of strings, so you can specify that in your pojo:
#JsonProperty(value = "optionsList")
private List<Map<String, List<String>>> optionsList;
with that, you can add one more iteration
for (GetAllRules rules : allRules) {
for (Map<String, List<String>> internal : rules.getOptionsList()) {
for (String value : internal.get(key)) {
System.out.println(value);
// will print "0001", ...
}
}
}
Nicer way to handle this would be to use Java streams - you don't want to use too many nested loops,
it may look like this:
allRules.stream()
.map(GetAllRules::getOptionsList)
.flatMap(Collection::stream)
.flatMap(option -> option.get(key).stream())
.forEach(System.out::println);

Using Jackson to implement toString() without annotations

I want to use Jackson to implement toString() to return the JSON representation of an object, but I do not want to use any Jackson annotation in my code.
I tried an implementation along the lines of:
public String toString()
{
Map<String,Object> ordered = ImmutableMap.<String, Object>builder().
put("createdAt", createdAt.toString()).
put("address", address.toString()).
build();
ObjectMapper om = new ObjectMapper();
om.enable(SerializationFeature.INDENT_OUTPUT);
try
{
return om.writeValueAsString(object);
}
catch (JsonProcessingException e)
{
// Unexpected
throw new AssertionError(e);
}
}
This works well for simple fields but if "address" has its own fields then instead of getting this:
{
"address" : {
"value" : "AZ4RPBb1kSkH4RNewi4NXNkBu7BX9DmecJ",
"tag" : null
}
I get this output instead:
{
"address" : "{\n\"value\" : \"AZ4RPBb1kSkH4RNewi4NXNkBu7BX9DmecJ\",\n \"tag\" : null"
}
In other words, the address value is being treated like a String as opposed to a JsonNode.
To clarify:
On the one hand, I want to control how simple class fields are converted to String. I don't want to use Jackson's built-in converter.
On the other hand, for complex fields, returning a String value to Jackson leads to the wrong behavior.
I believe that I could solve this problem by adding a public toJson() method to all my classes. That method would return a Map<String, JsonNode>, where the value is a string node for simple fields and the output of toJson() for complex fields. Unfortunately, this would pollute my public API with implementation details.
How can I achieve the desired behavior without polluting the class's public API?
UPDATE: I just saw an interesting answer at https://stackoverflow.com/a/9599585/14731 ... Perhaps I could convert the String value of complex fields back to JsonNode before passing them on to Jackson.
I think you should implement two methods in each class - one to dump data, second to build JSON out of raw data structure. You need to separate this, otherwise you will nest it deeper and deeper every time you encapsulate nested toString() calls.
An example:
class Address {
private BigDecimal yourField;
/* …cut… */
public Map<String, Object> toMap() {
Map<String, Object> raw = new HashMap<>();
raw.put("yourField", this.yourField.toPlainString());
/* more fields */
return raw;
}
#Override
public String toString() {
// add JSON processing exception handling, dropped for readability
return new ObjectMapper().writeValueAsString(this.toMap());
}
}
class Employee {
private Address address;
/* …cut… */
public Map<String, Object> toMap() {
Map<String, Object> raw = new HashMap<>();
raw.put("address", this.address.toMap());
/* more fields */
return raw;
}
#Override
public String toString() {
// add JSON processing exception handling, dropped for readability
return new ObjectMapper().writeValueAsString(this.toMap());
}
}

XmlHttpContent serializer sort fileds alphabetically

I need strict compliance with the order of the elements in my xml document. If I use XmlHttpContent serializer to form xml content, fields sort alphabetically.
Is there any way to specify explicitly order of the elements in xml? Or are there other ways to create and post http requests with the xml body?
I know this answer isn't ideal but I recently came across this issue when trying to use the http client library for serialisation to xml. The solution I've found that works is to have my DTO classes provide a method to convert them into a sorted map of some kind.
In my case this is an ImmutableMap<String, Object> as I'm also using Guava but any map with controllable order will do. The basic idea is to work with the java objects to construct your data but then when the time comes to serialise them you serialise the map instead.
public interface OrderedXml {
ImmutableMap<String, Object> toOrderedMap();
}
public class Parent implements OrderedXml {
#Key("First") String first;
#Key("Second") String second;
#Key("Child") Child third;
#Override
public ImmutableMap<String, Object> toOrderedMap() {
return ImmutableMap.of(
// the order of elements in this map will be the order they are serialised
"First", first,
"Second", second,
"Child", third.toOrderedMap()
);
}
}
public class Child implements OrderedXml {
#Key("#param1") String param1;
#Key("#param2") String param2;
#Key("text()") String value;
#Override
public ImmutableMap<String, Object> toOrderedMap() {
return ImmutableMap.of(
// the same goes for attributes, these will appear in this order
"#param1", param1,
"#param2", param2,
"text()", value
);
}
}
public class Main {
public static void main(String[] args) {
// make the objects
Parent parent = new Parent();
parent.first = "Hello";
parent.second = "World";
parent.child = new Child();
parent.child.param1 = "p1";
parent.child.param2 = "p2";
parent.child.value = "This is a child";
// serialise the object to xml
String xml = new XmlNamespaceDictionary()
.toStringOf("Parent", parent.toOrderedXml()); // the important part
System.out.println(xml); // should have the correct order
}
}
I know this solution isn't ideal but at least you can reuse the toOrderedXml to make a nice toString :-).

How can I serialize a list of objects using flex json?

I have a list of an object like follows
List<ProductInfo>
I want to serialize it using flex json so that it should like
[{"product_id":"2","name":'stack"'},{"product_id":"2","name":"overflow"}]"
for deserializing from the above string into a List of Objects I am using the following code
final List<ProductInformation> productInformationList = new JSONDeserializer<List<ProductInformation>>().use(null, ArrayList.class)
.use("values", ProductInformation.class).deserialize(parameterValue);
for serializing the object to string I am doing this but it's not working....I am missing something...
final String serializizedString = new JSONSerializer().serialize(productInformationList);
What do I need to serialize the object into a string?
List<ProductInfo> ls = new JSONDeserializer<ArrayList<ProductInfo>>().use("values", ProductInfo.class).deserialize(s);
Follow this link or read care fully following
Refactored path listings for Maps and Collections. In prior versions there was no way to specify both the concrete top of a Collection/Map AND the concrete class contained within. The path language was not verbose enough. Now you can specify both the concrete collection AND the concrete class contained within. if person.friends is a path that points to java.util.Map. For example,
new JSONDeserializer<Person>()
.use( "person.friends", HashMap.class )
.use("person.friends.keys", Relation.class )
.use( "person.friends.values", Person.class )
By adding "keys" and "values" to the path person.friends you can specify the actual concrete classes to use for the keys and values of the Map. For Collections you can simply append "values" to specify the containing class. For example:
new JSONDeserializer<List<Person>>().use( "people", ArrayList.class ).use("people.values", Person.class )
I've never played with flexjson before but after downloading it and playing with it here is what I've come up with:
public class TestFlexJson {
public static void main(String args[]) {
ProductInfo p1 = new ProductInfo(1, "Stack");
ProductInfo p2 = new ProductInfo(2, "Overflow");
List<ProductInfo> infos = Arrays.asList(p1, p2);
String s = new JSONSerializer()
.exclude("*.class", "description")
//.include("productId", "name")
// EDIT: the "include" call is irrelevant for this example.
.serialize(infos);
System.out.println(s);
// => [{"name":"Stack","productId":1},{"name":"Overflow","productId":2}]
List<ProductInfo> ls = new JSONDeserializer<List<ProductInfo>>().deserialize(s);
System.out.println(ls);
// => [{name=Stack, productId=1}, {name=Overflow, productId=2}]
}
public static class ProductInfo {
private int id;
private String name;
private String desc; // Not used, to demonstrate "exclude".
public ProductInfo(int id, String name) {
this.id = id;
this.name = name;
}
public int getProductId() { return this.id; }
public String getName() { return this.name; }
public String getDescription() { return this.desc; }
}
}
Seems to work for me.
Unfortunately, the class property must be included if you need to deserialize your json into a collection. In the above example, the json string was deserialized as follows:
List<ProductInfo> ls = new JSONDeserializer<List<ProductInfo>>().deserialize(s);
System.out.println(ls);
// => [{name=Stack, productId=1}, {name=Overflow, productId=2}]
If you were to try to access an element directly, lets say ls.get(0)y you would receive a ClassCastException: java.util.HashMap cannot be cast to ProductInfo.
You must serialize your object to include the class property in order to appropriately deserialize into a collection.

Categories