How to convert List<Object> into List<T>? - java

This is the code for getting retrofit response as an object. The below method is working fine but I need an one common function for performing the above functionality, i.e the class name may vary. (e.g) ticket, price, token, appointment like this:
processGETRequest(AppController.getApiHelper().searchTickets(from, to), new RetrofitListener() {
#Override
public void onSuccess(Object object) { }
#Override
public void onSuccess(List<Object> object) {
// Here I'm getting retrofit response as a object //
if (object != null) {
// Below method is working fine //
List<Ticket> ticketList = new ArrayList<>();
for (Object result : object) {
String json = new Gson().toJson(result);
Ticket model = new Gson().fromJson(json, Ticket.class);
ticketList.add(model);
}
// I need an one common function for performing above functionality
// i.e the Class name may vary.. (e.g) Ticket, Price, Token, Appointment like this.
}
}
#Override
public void onError(String error) {
Log.d("error: ", " " + error);
}
}, false);
The RetrofitListener interface is simply:
public interface RetrofitListener {
void onSuccess(Object object);
void onSuccess(List<Object> object);
void onError(String error);
}

You can use a static function similar to:
static <T> List<T> toList(List<Object> object, Class<T> desiredClass) {
List<T> transformedList = new ArrayList<>();
if (object != null) {
for (Object result : object) {
String json = new Gson().toJson(result);
T model = new Gson().fromJson(json, desiredClass);
transformedList.add(model);
}
}
return transformedList;
}
Basically you just need to ensure that you deliver the desired type (e.g. desiredClass) and use it in fromJson.
Sample usage:
List<Ticket> ticketList = toList(object, Ticket.class);
List<Price> priceList = toList(object, Price.class);
Note that by moving the object != null into the toList-method you do not need to care about what is passed to that method. You at least get an empty list in return.

<T> List<T> getList(Class<T> type, List<Object> object) {
return object.stream()
.map(result -> new Gson().toJson(result))
.map(new Gson().fromJson(json, type))
.collect(Collectors.toList());
}
List<Ticket> ticketList = getList(Ticket.class, object);
This will do what your for-loop did.

You can convert the snippet
if (object != null) {
List<Ticket> ticketList = new ArrayList<>();
for (Object result : object) {
String json = new Gson().toJson(result);
Ticket model = new Gson().fromJson(json, Ticket.class);
ticketList.add(model);
}
}
with generics like this
<T> void check(Class<T> type, List<Object> object) {
List<T> ticketList = new ArrayList<>();
for (Object result : object) {
String json = new Gson().toJson(result);
T model = new Gson().fromJson(json, type);
ticketList.add(model);
}
}

As according to your question you want a generic code to get List which can be of Any? type.
List<Ticket> ticketList = new ArrayList<>();
for (Object result : object) {
String json = new Gson().toJson(result);
Ticket model = new Gson().fromJson(json, Ticket.class);
ticketList.add(model);
}
is this what you want?
List<Ticket> ticketList // can be List<T>??
Then create a generic method to get list and use it any where:
public <T> List<T> getObjectToList(Object obj, Class<T[]> ObjectArryaClass) {
String json = new Gson().toJson(obj);
return Arrays.asList(new Gson().fromJson(json, ObjectArryaClass));
}
call above method as:
#Override
public void onSuccess(List<Object> object) {
// Here I'm getting retrofit response as a object //
if (object != null) {
// Below method is working fine //
List<Ticket> ticketList = getObjectToList(object,Ticket[].class)
// I need an one common function for performing above functionality
// i.e the Class name may vary.. (e.g) Ticket, Price, Token, Appointment like this.
}
}

Related

Cast result of multiple tasks list to their POJO in Tasks.whenAllSuccess() (Firestore)

List<Task<DocumentSnapshot>> fetchUserTasks = new ArrayList<>();
List<Task<DocumentSnapshot>> fetchLikeTasks = new ArrayList<>();
So I have created two Tasks Lists. If it is one Task List I can cast object to DocumentSnapshot and store in POJO like this:
Task combineUserTasks = Tasks.whenAllSuccess(fetchUserTasks).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> objects) {
for (Object object : objects) {
UserModel userModel = ((DocumentSnapshot) object).toObject(UserModel.class);
userModelArrayList.add(userModel);
}
});
But in case of two Task List how I can cast results for each List?
Tasks.whenAllSuccess(combineUserTasks, combineLikeTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> objects) {
// How to fetch/cast data for individual POJO here?
}
});
Update 1:
Tasks.whenAllSuccess(combineUserTasks, combineLikeTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> objects) {
for (Object object : objects) {
UserModel userModel = ((DocumentSnapshot) object).toObject(UserModel.class);
LikeModel likeModel = ((DocumentSnapshot) object).toObject(LikeModel.class);
userModelArrayList.add(userModel);
likeModelArrayList.add(likeModel);
}
}
});
As I understand, you have two types of lists, userModelArrayList which is of type UserModel, and likeModelArrayList which is of type LikeModel. To be able to add each object in its corresponding type of list, you should create the lists of Task objects to be of type UserModel and LikeModel. So please change the following lines of code:
List<Task<DocumentSnapshot>> fetchUserTasks = new ArrayList<>();
List<Task<DocumentSnapshot>> fetchLikeTasks = new ArrayList<>();
to
List<Task<UserModel>> fetchUserTasks = new ArrayList<>();
List<Task<LikeModel>> fetchLikeTasks = new ArrayList<>();
So instead of adding the DocumentSnapshot objects, add the actual objects. So use toObject() before. That being said, you can then check each object type using instanceof operator, like in the following lines of code:
Tasks.whenAllSuccess(combineUserTasks, combineLikeTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> objects) {
for (Object object : objects) {
if (object instanceof UserModel) {
userModelArrayList.add(userModel);
} else if (object instanceof LikeModel) {
likeModelArrayList.add(likeModel);
}
}
}
});
Now, you'll have each list populated with the corresponding object type.

Enum's ability to implement part of the business logic

Trying to refactor the code. Now the code is:
if ("objects".equals(type)) {
Object oldJson = oldData.get("content");
Object newJson = newData.get("content");
} else if ("objects.appeals".equals(type)) {
Object oldJson = oldData.get("data").get("person");
Object newJson = newData.get("data").get("person");
}
The number of types is much larger. I gave only 2 for an example. Trying to optimize with enum:
public enum HistoryUpdateTypeEnum {
OBJECTS("objects", new Document()),
APPEALS_OBJECTS("appeals.objects", new Document());
HistoryUpdateTypeEnum(String type, Document documentSlice) {
this.type = type;
this.documentSlice = documentSlice;
}
private String type;
private Document documentSlice;
public static HistoryUpdateTypeEnum fromString(String value) {
return Stream.of(values())
.filter(Objects::nonNull)
.filter(v -> v.name().replaceAll("_",".").equalsIgnoreCase(value))
.findAny()
.orElse(null);
}
public Object formSlice(Document data) {
this.documentSlice = data;
return documentSlice.get("content"); // How to make it universal?
}
}
And use:
HistoryUpdateTypeEnum typeEnum = HistoryUpdateTypeEnum.fromString("objects.appeals");
Document oldData = new Document(......).append(..., ...);
Document newData = new Document(......).append(..., ...);
Object oldJson = typeEnum.formSlice(oldData);
Object newJson = typeEnum.formSlice(newData);
I can’t figure out how to make me perform my action for each type. That is, documentSlice.get ("content") for 'objects' or documentSlice.get("data").get("person") for 'appeals.objects'. Are there any ideas?
One of the possible variants is abstract method in your Enum class:
public enum HistoryUpdateTypeEnum {
OBJECTS {
#Override
Object getJson(Document data) {
return data.get("objects");
}
},
...
abstract Object getJson(Document data);
}
Then you could use it in such way:
HistoryUpdateTypeEnum history = HistoryUpdateTypeEnum .valueOf(type.toUpperCase());
Object oldJson = history.getJson(oldData);
Object newJson = history.getJson(newData);

Map String Array into a POJO with Dozer

I'm trying to convert a List<String[]> into List<Object> using Dozer but unable to map the index values to the property fields using mapper API configuration.
How can I map the members of the String[] into individual object fields with each index targeting a specific field? (e.g. [0] -> name, and [1] -> role)
DozerBeanMapper mapper = new DozerBeanMapper();
BeanMappingBuilder builder = new BeanMappingBuilder() {
#Override
protected void configure() {
mapping(String[].class, User.class)
.fields(this_(), "name"); // HOW do I specify index?**
}
};
mapper.addMapping(builder);
List<String[]> users = new ArrayList<>();
String[] user1 = {"Jill", "SDE"};
String[] user2 = {"Jack", "PM"};
users.add(user1);
users.add(user2);
List<User> userList = mapObjects(mapper, users, User.class);
where mapObjects() is;
private static <T1, T2> List<T2> mapObjects(DozerBeanMapper mapper, List<T1> sourceList, Class<T2> destinationClazz) {
try {
return sourceList.stream()
.map(i -> mapper.map(i, destinationClazz))
.collect(Collectors.toList());
} catch (Exception e) {
...
}
return new ArrayList<>();
}
and User class;
class User {
String name;
String role;
// getter & setter
}
It worked perfectly with the following configuration;
DozerBeanMapper mapper = new DozerBeanMapper();
BeanMappingBuilder builder = new BeanMappingBuilder() {
#Override
protected void configure() {
mapping(String[].class, User.class)
.fields(this_(), "name", FieldsMappingOptions.customConverterId("arrToName"))
.fields(this_(), "role", FieldsMappingOptions.customConverterId("arrToRole"));
}
};
final Map<String, CustomConverter> customConverterMap = new HashMap<>();
customConverterMap.put("arrToName", new ArrToNameConverter());
customConverterMap.put("arrToRole", new ArrToRoleConverter());
mapper.setCustomConvertersWithId(customConverterMap);
mapper.addMapping(builder);
Utilizing a logic where String[] is mapped into name and role fields separately via custom converters, which are targeting a specific index of the input String[]. With dozer, you can essentially define custom converters and assign them an id, and refer them with those ids inside of field mappings FieldsMappingOptions.customConverterId("{id}")
where ArrToNameConverter;
public class ArrToNameConverter extends DozerConverter<String[], String> {
public ArrToNameConverter() {
super(String[].class, String.class);
}
#Override
public String convertTo(String[] strings, String user) {
return strings[0];
}
#Override
public String[] convertFrom(String user, String[] strings) {
return new String[0];
}
}
and ArrToRoleConverter;
public class ArrToRoleConverter extends DozerConverter<String[], String> {
public ArrToRoleConverter() {
super(String[].class, String.class);
}
#Override
public String convertTo(String[] strings, String user) {
return strings[1];
}
#Override
public String[] convertFrom(String user, String[] strings) {
return new String[0];
}
}
With the above mapper, I was able to get the following result;
[User(name=Jill, role=SDE), User(name=Jack, role=PM)]

How to convert dynamic JSON reponse with Java Gson library

I have an API that can return JSON arrays or objects. Example JSON object
{
"id": 1,
"name": "name"
}
JSON array:
[
{
"id": 1,
"name": "name"
},
{
"id": 1,
"name": "name"
}
]
When mapping a JSON object response to a POJO I use:
MyEntity myEntity = new Gson().fromJson(jsonString, MyEntity.class);
When mapping a JSON array response to an array of POJOs I use:
MyEntity[] myEntity = new GSON().fromJson(jsonString, MyEntity[].class);
How can I convert those two responses to the appropriate types dynamically?
NOTE: I can't modify the server response, this is a public API.
Thank you!
EDIT:
I am trying to implement a method that does this automatically but I am missing something. The method
public <T> T convertResponseToEntity(Class<T> classOfT)
{
JsonElement jsonElement = this.gson.fromJson(getResponseAsString(), JsonElement.class);
if (jsonElement.isJsonArray()) {
Type listType = new TypeToken<T>(){}.getType();
return this.gson.fromJson(getResponseAsString(), listType);
}
return this.gson.fromJson(getResponseAsString(), (Type) classOfT);
}
It returns a list of LinkedTreeMaps. How can I modify the code to return the same content as Object[]?
How can I convert those 2 responses dynamically to the appropriate type?
It depends on how to interpret the "appropriate type" here because it would lead to instanceof or visitor pattern to get the appropriate type once you try to handle the parsed-from-JSON object every time you need it. If you can't change the API, you can smooth the way you use it. One of possible options here is handling such response as if everything is a list. Even a single object can be handled as a list with one element only (and many libraries work with sequences/lists only having that fact: Stream API in Java, LINQ in .NET, jQuery in JavaScript, etc).
Suppose you have the following MyEntity class to handle the elements obtained from the API you need:
// For the testing purposes, package-visible final fields are perfect
// Gson can deal with final fields too
final class MyEntity {
final int id = Integer.valueOf(0); // not letting javac to inline 0 since it's primitive
final String name = null;
#Override
public String toString() {
return id + "=>" + name;
}
}
Next, let's create a type adapter that will always align "true" lists and single objects as if it were a list:
final class AlwaysListTypeAdapter<T>
extends TypeAdapter<List<T>> {
private final TypeAdapter<T> elementTypeAdapter;
private AlwaysListTypeAdapter(final TypeAdapter<T> elementTypeAdapter) {
this.elementTypeAdapter = elementTypeAdapter;
}
static <T> TypeAdapter<List<T>> getAlwaysListTypeAdapter(final TypeAdapter<T> elementTypeAdapter) {
return new AlwaysListTypeAdapter<>(elementTypeAdapter);
}
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final List<T> list)
throws IOException {
if ( list == null ) {
out.nullValue();
} else {
switch ( list.size() ) {
case 0:
out.beginArray();
out.endArray();
break;
case 1:
elementTypeAdapter.write(out, list.iterator().next());
break;
default:
out.beginArray();
for ( final T element : list ) {
elementTypeAdapter.write(out, element);
}
out.endArray();
break;
}
}
}
#Override
public List<T> read(final JsonReader in)
throws IOException {
final JsonToken token = in.peek();
switch ( token ) {
case BEGIN_ARRAY:
final List<T> list = new ArrayList<>();
in.beginArray();
while ( in.peek() != END_ARRAY ) {
list.add(elementTypeAdapter.read(in));
}
in.endArray();
return unmodifiableList(list);
case BEGIN_OBJECT:
return singletonList(elementTypeAdapter.read(in));
case NULL:
return null;
case END_ARRAY:
case END_OBJECT:
case NAME:
case STRING:
case NUMBER:
case BOOLEAN:
case END_DOCUMENT:
throw new MalformedJsonException("Unexpected token: " + token);
default:
// A guard case: what if Gson would add another token someday?
throw new AssertionError("Must never happen: " + token);
}
}
}
Gson TypeAdapter are designed to work in streaming fashion thus they are cheap from the efficiency perspective, but not that easy in implementation. The write() method above is implemented just for the sake of not putting throw new UnsupportedOperationException(); there (I'm assuming you only read that API, but don't know if that API might consume "either element or a list" modification requests). Now it's necessary to create a type adapter factory to let Gson pick up the right type adapter for every particular type:
final class AlwaysListTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory alwaysListTypeAdapterFactory = new AlwaysListTypeAdapterFactory();
private AlwaysListTypeAdapterFactory() {
}
static TypeAdapterFactory getAlwaysListTypeAdapterFactory() {
return alwaysListTypeAdapterFactory;
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken)
throws IllegalArgumentException {
if ( List.class.isAssignableFrom(typeToken.getRawType()) ) {
final Type elementType = getElementType(typeToken);
// Class<T> instances can be compared with ==
final TypeAdapter<?> elementTypeAdapter = elementType == MyEntity.class ? gson.getAdapter(MyEntity.class) : null;
// Found supported element type adapter?
if ( elementTypeAdapter != null ) {
#SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) getAlwaysListTypeAdapter(elementTypeAdapter);
return castTypeAdapter;
}
}
// Not a type that can be handled? Let Gson pick a more appropriate one itself
return null;
}
// Attempt to detect the list element type
private static Type getElementType(final TypeToken<?> typeToken) {
final Type listType = typeToken.getType();
return listType instanceof ParameterizedType
? ((ParameterizedType) listType).getActualTypeArguments()[0]
: Object.class;
}
}
And how it's used after all:
private static final Type responseItemListType = new TypeToken<List<MyEntity>>() {
}.getType();
private static final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(getAlwaysListTypeAdapterFactory())
.create();
public static void main(final String... args) {
test("");
test("{\"id\":1,\"name\":\"name\"}");
test("[{\"id\":1,\"name\":\"name\"},{\"id\":1,\"name\":\"name\"}]");
test("[]");
}
private static void test(final String incomingJson) {
final List<MyEntity> list = gson.fromJson(incomingJson, responseItemListType);
System.out.print("LIST=");
System.out.println(list);
System.out.print("JSON=");
gson.toJson(list, responseItemListType, System.out); // no need to create an intermediate string, let it just stream
System.out.println();
System.out.println("-----------------------------------");
}
The output:
LIST=null
JSON=null
-----------------------------------
LIST=[1=>name]
JSON={"id":1,"name":"name"}
-----------------------------------
LIST=[1=>name, 1=>name]
JSON=[{"id":1,"name":"name"},{"id":1,"name":"name"}]
-----------------------------------
LIST=[]
JSON=[]
-----------------------------------
Just parse it into JsonElement and check actual element type:
Gson g = new Gson();
JsonParser parser = new JsonParser();
JsonElement e = parser.parse( new StringReader(jsonString) );
if(e instanceof JsonObject) {
MyEntity myEntity = g.fromJson(e, MyEntity.class);
} else {
MyEntity[] myEntity = g.fromJson(e, MyEntity[].class);
}

Applying a list of values as predicate using Collection Utils by the use of pedicates

I want to implement Database systems in functionality by using the predicate.
This is as like if SQL filter a recordset by in it cumbersome the results.
But if i pass the List as in predicate it takes only one value i.e. if i am passing 53 and 54 it filter the results for 53 only.
public class classNamePredicate implements Predicate<className> {
private Object expected1;
private String property;
private List<Object> listOfValues = new ArrayList<Object>();
public SalesOrderPredicate(Object expected1, String property) {
super();
this.expected1 = expected1;
this.property = property;
}
public SalesOrderPredicate(List<Object> listValues, String property) {
this.listOfValues = listValues;
this.property = property;
}
#Override
public boolean evaluate(SalesOrder object) {
try {
if (property.equals("volume")) {
return ((Integer) expected1 < object.getVolume());
}
if (property.equals("startDateId")) {
return (expected1.equals(object.getStartDateId()));
}
if (property.equals("endDateId")) {
return (expected1.equals(object.getEndDateId()));
}
if (property.equals("productIds")) {
for (Object value : listOfValues) {
return (object.getProductId() == (Integer) value);
}
}
if (property.equals("sourceIds")) {
for (Object value : listOfValues) {
return (object.getSourceId() == (Integer) value);
}
}
return false;
} catch (Exception e) {
return false;
}
}
}
I am trying to use this as per the following way:
List<Object> productIds = new ArrayList<Object>();
productIds.add(53);
productIds.add(54);
List<Object> sourceIds = new ArrayList<Object>();
sourceIds.add(122);
Predicate[] classnameOrderPredicate = { (Predicate) new classnamePredicate(4415, "startDateId"),
(Predicate) new classnamePredicate(4443, "endDateId"), (Predicate) new classnamePredicate(100000, "volume"),
(Predicate) new classnamePredicate(productIds, "productIds"), (Predicate) new classnamePredicate(sourceIds, "sourceIds") };
Predicate classnameallPredicateGeneric = (Predicate) PredicateUtils
.allPredicate((org.apache.commons.collections4.Predicate<? super classname>[]) classnamePredicate);
Collection<classname> classnamefilteredCollectionGeneric = GenericCollectionUtils.select(classname, classnameallPredicateGeneric);
Please suggest in design perspective too.
Thanks in advance
You're only evaluating the first item in the collection:
for (Object value : listOfValues) {
return (object.getProductId() == (Integer) value);
}
You want to evaluate all of them, and Java conveniently provides a contains() method for that:
return listOfValues.contains(object.getProductId());
Other than that, the code looks pretty awful, you should create smaller, targeted Predicates, instead of writing a generic one with lots of different cases. You could get rid of those casts at the same time.
You also failed at your obfuscation by failing to replace a few SalesOrder by className (which doesn't respect the Java coding standard and is distracting).

Categories

Resources