mapping from flat protobuf message to class with inner classes using mapStruct - java

I need to use mapStruct to write a mapping from message AddressProto to the class Address. But message AddressProto consists only of string fields while Address class has inner classes.
I have written such a mapper so far, but due to the difference between the message structure and the class, I don’t know how to correctly map fields from message AddressProto to class Address and back.
#Mapper(config = MapstructConfig.class, unmappedTargetPolicy = ReportingPolicy.ERROR)
public abstract class AddressProtoMapper {
// from proto to object
public abstract Address mapToAddress(AddressProto address);
// from object to proto
public abstract AddressProto mapAddressToProto(Address address);
}
proto message AddressProto (after the slash for each field, I wrote in which class field it needs to be mapped):
message AddressProto {
string value = 1; // Address.AddressValue.value
string unrestricted_value = 2; // Address.AddressValue.unrestrictedValue
string country = 3; // Address.Structure.Country.name
string country_iso_code = 4; // Address.Structure.Country.isoCode
string region = 5; // Address.Structure.Region.name
}
java class Address:
public class Address {
public final AddressValue value;
public final Structure structure;
public static class AddressValue {
public final String value;
public final String unrestrictedValue;
}
public static class Structure {
public final Country country;
public final Region region;
public static class Country {
public final String name;
public final String isoCode;
}
public static class Region {
public final String name;
}
}
}

The solution turned out to be very simple. Even too much :)
You just need to write your own implementation.
#Mapper(
config = MapstructConfig.class,
unmappedTargetPolicy = ReportingPolicy.ERROR
)
public interface AddressProtoMapper {
default Address mapToAddress(AddressProto address){
return new Address(
...
);
}
default AddressProto mapAddressToProto(Address address) {
return new AddressProto(
...
);
}
}

Related

How can I print an arraylist that is in one class, by a grouping from another class?

I'm trying to print an arraylist that is in one class, based on one of the parameters from another class. Is this possible?
import java.util.ArrayList;
public class TVShow {
private String title;
private String summary;
private String releaseDate;
private ArrayList<Episode> episodeList;
public TVShow(String title, String summary, String releaseDate) {
this.title = title;
this.summary = summary;
this.releaseDate = releaseDate;
this.episodeList = new ArrayList<>();
}
public void addEpisode(Episode episode) {
episodeList.add(episode);
}
public printEpisodesInSeason(int seasonNr) {
// How can I make this method access the other class and
// print the episodeList by season number?
for (Episode episode : episodeList) {
return System.out.println(episode.);
}
}
}
public class Episode {
private int episodeNr;
private int seasonNr;
private String eTitle;
private int runTime;
public Episode(int episodeNr, int seasonNr, String eTitle, int runTime) {
this.episodeNr = episodeNr;
this.seasonNr = seasonNr;
this.eTitle = eTitle;
this.runTime = runTime;
}
}
EDIT: I think I misinterpreted the question. You want to only print the episodes from a specific season. This can be done by applying the filter function on the episodeList as follows:
for (Episode episode : episodeList.stream().filter(episode -> episode.getSeasonNr() == seasonNr).collect(Collectors.toList()))
{ ... }
This is ofcourse assuming you apply the getter setter pattern as described below before I edited the answer.
The filter function takes an anonymous function and applies it to all members of a collection. This way, only the episodes which have a season number that is supplied by the user are returned. Then, the foreach loop iterates over the resulting collection.
You could either make the members of Episode public by defining:
public class Episode {
public int episodeNr;
public int seasonNr;
public String eTitle;
public int runTime;
public Episode(int episodeNr, int seasonNr, String eTitle, int runTime) {
this.episodeNr = episodeNr;
this.seasonNr = seasonNr;
this.eTitle = eTitle;
this.runTime = runTime;
}
}
But this is seen as bad practice. The better way to do it is by defining methods in your Episode class to return the value of the class' fields like for example:
public class Episode {
public int episodeNr;
public int seasonNr;
public String eTitle;
public int runTime;
public Episode(int episodeNr, int seasonNr, String eTitle, int runTime) {
this.episodeNr = episodeNr;
this.seasonNr = seasonNr;
this.eTitle = eTitle;
this.runTime = runTime;
}
public String getTitle() {
return this.eTitle;
}
}
This practice is called getters and setters and it positively impacts the encapsulation of the code. You could then obtain the value of the Episode's members by calling, for example episode.getTitle().

How to assign a value to a final static variable from a child inside java?

Suppose we have a hierarchy like this:
class Parent {
public static final String BASE_NAME = "parent";
public static final String X_URL = BASE_NAME + "/x";
public static final String Y_URL = BASE_NAME + "/y";
}
class ChildA extends Parent {
public static final String BASE_NAME = "childA";
public static final String X_URL = BASE_NAME + "/x";
public static final String Y_URL = BASE_NAME + "/y";
#RequestMapping(value= X_URL)
public String renderXPage(){...}
}
as you can see the X_URL and Y_URL are repeating both in parent and child, if the child can feed in its own BASE_NAME to the parent, we can eliminate redundant repetition of those constants and then we can use them for the annotation value. How to do this in Java ?
There is no way to reliable achieve this.
You can have #RequestMapping annotation on class and put base part of URL there.
Example:
#RequestMapping("ChildA")
class ChildA {
#RequestMapping(value= "x")
public String renderXPage(){...}
}
renderXPage will handle "ChildA/x" URL.
Using the solution of #talex I came up with this neat solution:
public interface CommonRelativeUrls {
String X_URL = "/x";
String Y_URL = "/y";
}
public interface Entities {
String CLASS_A = "class-a";
String CLASS_B = "class-b";
...
}
#RequestMapping(value = Entities.CLASS_A)
public class ClassA implements CommonRelativeUrls {
#RequestMapping(value= X_URL)
public String renderXPage(){...}
}

Object to string delimited format

I have set of objects of different types.
Ex : Employee emp, adress adr
These two classes have list of properties
public class Employee{
private Stringname;
private int age;
}
public class Adress {
private String HouseNo;
private string Street;
private string pin;
}
Each attribute is assigned with some 2 character value
Name (NA), age (AG), HouseNo(HN),Street(ST), pin(PN)
I need to construct a string with these data and delimit with a %
Output:
NA%Vidhya%AG%30%HN%80%ST%1st cross%PN%100100
Each class knows it own data best so I would let each class be responsible for generating the string. As I understand it the two char codes for each field are unique for each class and member and only used when generating the string so only the class would need them.
interface AttributeDescription {
String generateDescription();
}
public class Employee implements AttributeDescription {
//members...
public String generateDescription() {
return String.format(“NA%%%s%%AG%%%d”, name, age)
}
Then simply call this method for all objects implementing the interface.
AttributeDescription object = ...
String attr = object.generateDescription();
I don't think it can be generalized more than this given the requirements.
Update
It might be better to have a builder class for building the string to get a more unified behavior between classes. Here is an example
public class AttributeBuilder {
private builder = new StringBuilder();
public String getAttribute() {
return builder.toString();
}
public void add(String code, String value) {
if (value == null) {
return;
}
builder.append(code);
builder.append(‘%’);
builder.append(value);
builder.append(‘%’);
}
}
And then you would also have to implement add(...) methods for other data types in a similar fashion. The builder could then be used like
public String generateDescription() {
AttributeBuilder builder = new AttributeBuilder();
builder.add(“NA”, name);
builder.add(“AG”, age);
return builder.getAttribute();
}

How to subclass a Java builder class?

I have two builders - PayloadA and PayloadB. To make example simpler, I have removed lot of other fields.
PayloadA.Builder constructor takes processName, genericRecord as an input parameter and then extract few things from genericRecord. And on that I am doing validation.
PayloadB.Builder constructor also takes processName, genericRecord as an input parameter and then it extract few different things from genericRecord as compared to above. And on those different fields I am doing validation.
As you can see, common thing between those two Payload?.Builder is processName, genericRecord, extracting oldTimestamp value and then isValid method.
Below is my PayloadA class:
public final class PayloadA {
private final String clientId;
private final String deviceId;
private final String processName;
private final GenericRecord genericRecord;
private final Long oldTimestamp;
private PayloadA(Builder builder) {
this.clientId = builder.clientId;
this.deviceId = builder.deviceId;
this.processName = builder.processName;
this.genericRecord = builder.genericRecord;
this.oldTimestamp = builder.oldTimestamp;
}
public static class Builder {
private final String processName;
private final GenericRecord genericRecord;
private final String clientId;
private final String deviceId;
private final Long oldTimestamp;
public Builder(PayloadA payload) {
this.processName = payload.processName;
this.genericRecord = payload.genericRecord;
this.clientId = payload.clientId;
this.deviceId = payload.deviceId;
this.oldTimestamp = payload.oldTimestamp;
}
public Builder(String processName, GenericRecord genericRecord) {
this.processName = processName;
this.genericRecord = genericRecord;
this.clientId = (String) DataUtils.parse(genericRecord, "clientId");
this.deviceId = (String) DataUtils.parse(genericRecord, "deviceId");
this.oldTimestamp = (Long) DataUtils.parse(genericRecord, "oldTimestamp");
}
// calling this method to validate
public boolean isValid() {
return isValidClientIdDeviceId();
}
private boolean isValidClientIdDeviceId() {
// validate here
}
public PayloadA build() {
return new PayloadA(this);
}
}
// getter here
}
Below is my PayloadB class:
public final class PayloadB {
private final GenericRecord genericRecord;
private final String processName;
private final String type;
private final String datumId;
private final Long oldTimestamp;
private PayloadB(Builder builder) {
this.processName = builder.processName;
this.genericRecord = builder.genericRecord;
this.type = builder.type;
this.datumId = builder.datumId;
this.oldTimestamp = builder.oldTimestamp;
}
public static class Builder {
private final GenericRecord genericRecord;
private final String processName;
private final String type;
private final String datumId;
private final Long oldTimestamp;
public Builder(PayloadB payload) {
this.processName = payload.processName;
this.genericRecord = payload.genericRecord;
this.type = payload.type;
this.datumId = payload.datumId;
this.oldTimestamp = payload.oldTimestamp;
}
public Builder(String processName, GenericRecord genericRecord) {
this.processName = processName;
this.genericRecord = genericRecord;
this.type = (String) DataUtils.parse(genericRecord, "type");
this.datumId = (String) DataUtils.parse(genericRecord, "datumId");
this.oldTimestamp = (Long) DataUtils.parse(genericRecord, "oldTimestamp");
}
// calling this method to validate
public boolean isValid() {
return isValidType() && isValidDatumId();
}
private boolean isValidType() {
// validate here
}
private boolean isValidDatumId() {
// validate here
}
public PayloadB build() {
return new PayloadB(this);
}
}
// getter here
}
Now is there any way I can use concept of abstract class here? I can create an abstract class Payload but what should be the stuff inside my abstract class:
public final class PayloadA extends Payload { ... }
public final class PayloadB extends Payload { ... }
And then once I build both my builder, I will pass it to some other method and there I want to access all the fields using getters. So let's say I have build PayloadA so I will send to execute method as shown below and then in that method, I want to extract all the fields of PayloadA. Similarly if I send PayloadB to execute method, then I want to extract all the fields of PayloadB class using getters. How can I do this?
private void execute(Payload payload) {
// How can I access fields of PayloadA or PayloadB
// depending on what was passe
}
Create a super class for the payloads only if the mentioned fields are not common by a coincidence. You can move common fields and methods (but not the builders) in there. You could even create a super class for the builders but it will probably clutter the code too much.
If you really have a use for the payload super class then you can implement your execute method with the Visitor Pattern:
First, you have to create a visitor where you can access your concrete classes:
public class PayloadVisitor {
public void visit(PayloadA payloadA) {
// use payload A here
}
public void visit(PayloadB payloadB) {
// use payload B here
}
}
Then you have to add a method to your super class accepting the visitor:
public abstract class Payload {
// common fields and methods
public abstract void accept(PayloadVisitor visitor);
}
Override the method accept in the subclasses:
public final class PayloadA extends Payload {
// ...
#Override
public void accept(PayloadVisitor visitor) {
visitor.visit(this);
}
}
public final class PayloadB extends Payload {
// ...
#Override
public void accept(PayloadVisitor visitor) {
visitor.visit(this);
}
}
Your method execute just redirects the call to the according visit method:
private void execute(Payload payload) {
payload.accept(new PayloadVisitor());
}
The visitor pattern can be overwhelming. You can also keep it simple and use instanceof to determine the concrete class.
I think the question here is if PayloadA and PayloadB are sharing something meaning full together for the design. If the logic is somehow the same except one parameter then you can have one class.
Maybe you can have the abstract class, and for the implementation for a specific field you can return your concrete value for a specific implementation.
For example Abstract class has abstract setter/getter for a field and when you implement that method to PayloadA and PayloadB you can return the field you want.
I think the problem is the design here not how to do it. See what your classes really are and then you have many options

How to deserialize JSON Array contained an abstract class without modifying a parent class?

I'm trying to deserialize JSON Array, which is persisted into my MongoDB, to a Java object by using Jackson. I found many tutorials mentioned to handle this polymorphism by adding:
#JsonTypeInfo(use=Id.CLASS,property="_class")
to a Super-class. However, in my case, I can't be able to modify the Super-class. So, are there some solutions to solve it without modifying the Super-class? Here is my code:
public class User {
#JsonProperty("_id")
private String id;
private List<Identity> identities; // <-- My List contains objects of an abstract class; Identity
public User(){
identities = new ArrayList<Identity>();
}
public static Iterable<User> findAllUsers(){
return users().find().as(User.class); // Always give me the errors
}
/*More code*/
}
It always give me the error - Can not construct instance of securesocial.core.Identity, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information.
You can use #JsonDeserilize annotation to bind a concrete implementation class to an abstract class. If you cannot modify your abstract class you can use the Jackson Mix-in annotations to tell Jackson how to find the implementation class.
Here is an example:
public class JacksonAbstract {
public static class User {
private final String id;
private final List<Identity> identities;
#JsonCreator
public User(#JsonProperty("_id") String id, #JsonProperty("identities") List<Identity> identities) {
this.id = id;
this.identities = identities;
}
#JsonProperty("_id")
public String getId() {
return id;
}
public List<Identity> getIdentities() {
return identities;
}
}
public static abstract class Identity {
public abstract String getField();
}
#JsonDeserialize(as = IdentityImpl.class)
public static abstract class IdentityMixIn {
}
public static class IdentityImpl extends Identity {
private final String field;
public IdentityImpl(#JsonProperty("field") String field) {
this.field = field;
}
#Override
public String getField() {
return field;
}
}
public static void main(String[] args) throws IOException {
User u = new User("myId", Collections.<Identity>singletonList(new IdentityImpl("myField")));
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Identity.class, IdentityMixIn.class);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(u);
System.out.println(json);
System.out.println(mapper.readValue(json, User.class));
}
}

Categories