getting an object data inside an object in java - java

i have a x object that contains a object inside , and i would like to acess it from a method without extra write code.
Let's say this is my object :
{
"status": "Success",
"code": "200 OK",
"message": "OK",
"data": {
"renter": {
....some Hashmap information.......
}
}
}
lets say i want to access the information the object renter inside the object data brings .
For that
in my method Y i did this :
#Override
public Y methodY(String userName) throws Exception{
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> clienteFeignOnMap= client.findByRenterName(userName);
//////////ACCESSING THE OBJECT DATA//////////////////
Map<String,Object> objectResponseData= (Map<String, Object>) clienteFeignOnMap.get("data");
//////////ACCESSING THE OBJECT RENTER INSIDE DATA FOR DESERIALIZING TO TyPE RENTER//////////////////
Renter renter = mapper.convertValue(objectResponseData.get("renter"), new TypeReference<Renter>(){ });
......more code.............
}
This way is successful and works, but still i think i could find a better option that allows me on one line resume that access from the inner object renter .
Thanks in advance for any help or suggestion. Happy ne year 2021

If you don't want to create a Java class to map the attributes as #Migwel suggested and want to use java.util.Map, then you may have to use Apache BeanUtils or something equivalent to get to deep elements.
BeanUtils.getNestedProperty( map, elementPath )
where elementPath is a dot separated path to the element. data.renter, in your case.
PropertyUtils.getNestedProperty( map, elementPath ) also does the same thing.
However, these will not let you access indexed properties, in case you have arrays within the objects. For that, as far as I know, you will have to write custom code akin to the method below.
public static <T> T getMapElement( Map<String, ? super Object> map, String path ) {
if( isEmpty( path ) ) return null;
String[] parts = path.split( "[\\[\\]\\.]" );
Object currElem = null;
try{
currElem = map;
for( int i = 0; i < parts.length; i++ ){
String part = parts[ i ];
boolean done = false;
/* Handle List. */
if( currElem instanceof List ) {
int index = integer( part, -1 );
if( index >= 0 ) {
currElem = ( (List) currElem ).get( index );
done = true;
}
}
if( done ) continue;
/* Handle Map. */
if( currElem instanceof Map ) currElem = ( (Map) currElem ).get( part );
if( currElem == null ) break;
};
}
catch( Exception e ){}
return (T) currElem;
}
Edit
There is also json-path from group com.jayway.jsonpath that also allows one to inspect a JSON directly into deep elements. Disclaimer: I haven't used and am not sure what it uses to deserialize the JSON. However, it may be worth a research.

Instead of using maps, you should create proper objects.
For example:
public class Response {
private String status;
private String code;
private String message;
private Data data;
// constructor, getters, setters
...
}
And so on with a class Data and a class Renter, etc.
Then, when using objectMapper, you can directly deserialize it into your Response object

Related

How to remove ONE of the duplicates in an ArrayList in Java

I have an ArrayList filled with a number of the same books. Now I need to be able to implement a method lendBook (String bookName, String lenderName) that removes the last one of this list. If I use books.get(bookName).remove(book) nothing happens. If I use books.get(bookName).remove(books.get(bookName).get(books.get(bookName).size()-1)) it removes the whole ArrayList. Thanks for your help in advance.
public class Library extends HashMap {
public Map<String, ArrayList<Book>> books;
protected Map<String, Lender> lenders;
public Library() {
this.books = new HashMap<String, ArrayList<Book>>();
this.lenders = new HashMap<String, Lender>();
}
public void addBook (String bookName){
String key = bookName;
if (books.containsKey(key)){
books.get(key).add(new Book(bookName));
} else {
books.put(bookName, new ArrayList<Book>());
books.get(key).add(new Book(bookName));
}
}
public void addLender (String lenderName) throws java.lang.IllegalStateException {
String key = lenderName;
if (lenders.containsKey(key)){
throw new java.lang.IllegalStateException(lenderName);
} else {
lenders.put(lenderName, new Lender(lenderName));
}
}
}
One solution might be to check for the last index in the ArrayList. You can see the size of the ArrayList with ArrayList.size(). So for instance
int size = ArrayList.size();
Then remove the last index.
The data structure you are using to store the books is a hashmap of String and an arraylist. Assuming the string to be book Name and arrayList containing of multiple book objects of the same book from which you want to delete last book
ArrayLiat<Book> booksForName = books.get(bookName);
Now to remove the last element of this list you just need to do
booksForName.removes(books.size()-1);
This can simply be done in a single line but the code will. be messy and not so easy to understand if you try reading after a few weeks :P
First, as commented, you should not be extending HashMap. Plus, I do not see how you were using Library as a Map.
Your use of the word "duplicates" in the title is misleading, as we are not dealing with duplicates of identical data. Instead, apparently you want to track multiple copies of a book.
You need to think through the problem domain to arrive at a proper data structure. A book is a concept (and a piece of intellectual property), with a title, an author(s), an identifier (ISBN etc.), a classification (such a Dewey Decimal number), and so on. Individual physical copies of a book are something else. Attributes of a copy include the identifier stamped on each by a librarian, and its condition as noted by a librarian. In our data model we would track the BookCopy objects as children related to a parent Book object.
Your desire that the lending method "removes the last one of this list" must mean that you want to lend our books per a LIFO policy: Last one in is first one out. Rather than use ArrayList as your collection, use an implementation of the interface meant for this purpose: Deque. ArrayDeque might be a good implementation for your purpose.
Actually, Deque is a bit confusing, as it can be used in different ways. For LIFO (Last-in, First-out) behavior, call:
Deque::addFirst
Deque::removeFirst
As for adding items to your maps, your books and lenders maps are what is known as a multimap. The "multi" refers to multiple values per key rather than a single value per key.
Java now has built-in support for multimaps via the computeIfAbsent feature added to Java 8.
Take for example this BookCopy class. We use enum Condition for a librarian to record the physical condition of each book copy. We use a UUID value as an identifier to be stamped on each book by the librarian.
package work.basil.example;
import java.util.Objects;
import java.util.UUID;
public class BookCopy
{
public enum Condition
{ PRISTINE, GOOD, SERVICABLE, DELAPIDATED }
;
// Member fields
private UUID uuid;
private Condition condition;
// Constructor
public BookCopy ( UUID uuid , Condition condition )
{
this.uuid = Objects.requireNonNull( uuid );
this.condition = Objects.requireNonNull( condition );
}
// Accessors
public UUID getUuid ( ) { return this.uuid; }
public Condition getCondition ( ) { return this.condition; }
public void setCondition ( Condition condition ) { this.condition = condition; }
// Object
#Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
BookCopy bookCopy = ( BookCopy ) o;
return uuid.equals( bookCopy.uuid );
}
#Override
public int hashCode ( )
{
return Objects.hash( uuid );
}
#Override
public String toString ( )
{
return "BookCopy{ " +
"uuid=" + uuid +
" | condition=" + condition +
" }";
}
}
Apparently you want to track which copies are available at the library for lending. For each book, we need a Deque containing copies available for loan. If we track each book by its title as a String, we would have a Map where the key is String (title), and the value is a Deque of BookCopy objects.
Map < String , Deque < BookCopy > > copiesOnHand = new HashMap <>();
As mentioned above, you really should have a Book class in addition to a BookCopy class. If so, would use a Book object rather than merely the title String.
Map < Book , Deque < BookCopy > > copiesOnHand = new HashMap <>();
But for the sake of simplicity in this short demo, we will stick to using a String title to represent the book.
First, we add book copies to that map. The computeIfAbsent line first checks to see if your specified key has an entry in the map with a non-null value. If not, the value (an empty ArrayDeque) is instantiated and added.
// Two copies of this book to lend.
String title = "Java Concurrency in Practice";
BookCopy bookCopy1 = new BookCopy( UUID.fromString( "61f036b5-da62-41fe-a765-f76bc31eebcb" ) , BookCopy.Condition.PRISTINE );
copiesOnHand.computeIfAbsent(
title , // key.
( String key ) -> new ArrayDeque <>()
)
.addFirst( bookCopy1 ); // value.
BookCopy bookCopy2 = new BookCopy( UUID.fromString( "731010a2-623a-4200-963a-9ce18f4fe988" ) , BookCopy.Condition.SERVICABLE );
copiesOnHand.computeIfAbsent(
title , // key.
( String key ) -> new ArrayDeque <>()
)
.addFirst( bookCopy2 ); // value.
Lending means we remove and return the last item in the deque.
// Debug: See our current inventory.
System.out.println( "copiesOnHand = " + copiesOnHand );
// Lend a book.
BookCopy copyToLend = copiesOnHand.computeIfAbsent(
title , // key.
( String key ) -> new ArrayDeque <>()
)
.removeFirst();
if ( Objects.nonNull( copyToLend ) )
{
System.out.println( "Lending book copy ID: " + copyToLend.getUuid() );
} else
{
System.out.println( "Sorry, no copies of that book available to lend. " );
}
// Debug: Review our inventory after lending.
System.out.println( "copiesOnHand = " + copiesOnHand );
When run.
copiesOnHand = {Java Concurrency in Practice=[BookCopy{ uuid=731010a2-623a-4200-963a-9ce18f4fe988 | condition=SERVICABLE }, BookCopy{ uuid=61f036b5-da62-41fe-a765-f76bc31eebcb | condition=PRISTINE }]}
Lending book copy ID: 731010a2-623a-4200-963a-9ce18f4fe988
copiesOnHand = {Java Concurrency in Practice=[BookCopy{ uuid=61f036b5-da62-41fe-a765-f76bc31eebcb | condition=PRISTINE }]}

Creating Composite Comparator in runtime for Sorting

My sample request
{
"requestModel":{
"CUSTID": "100"
},
"returnParameters":[
{
"name":"NETWORK/NETID",
"datatype":"String",
"order":"asc",
"sequence":1
},
{
"name":"INFODATA/NAME",
"datatype":"String",
"order":"asc",
"sequence":1
},
{
"name":"SOURCE/SYSTEM",
"datatype":"int",
"order":"asc",
"sequence":2
},
]
}
Sample Response
Below is my dynamically generated Map format of json response[Response parameters will be different each time based on the request params],
"responseModel":{
"documents": [
{
"NETWORK":[
{"NETID":"1234"},
{"ACT":"300"}
],
"SOURCE": {
"SYSTEM":"50"
},
"INFODATA":{
"NAME":"PHIL"
}
},
{
"NETWORK":[
{"NETID":"1234"},
{"ACT":"300"}
],
"SOURCE": {
"SYSTEM":"100"
},
"INFODATA":{
"NAME":"PHIL"
}
}
]
}
Problem Statement
I need to do multi level sorting based on the "returnParameters" in the request which is dynamic...
"order" indicates ascending (or) descending and sequence indicates the the priority for ordering like (group by in sql query)
Code
Map<String,Object> documentList = new HashMap<String,Object>();
JSONObject jsonObject= new JSONObject(response.getContent());
response.getContent() -> is nothing but it contains the above json response in Map format.
Now I converting the map to list of json object
JSONArray jsonArray= (JSONArray)jsonObject.get("documents");
ArrayList<JSONObject> list = new ArrayList<>();
for(int i=0;i<jsonArray.length();i++){
list.add((JSONObject) jsonArray.get(i));
}
Collections.sort(list, new ResponseSorter());
public class ResponseSorter implements Comparator<JSONObject> {
#Override
public int compare(JSONObject o1,JSONObject o2){
String s1= (String)((JSONObject) o1.get("NETWORK")).get("NETID");
String s2= (String)((JSONObject) o2.get("NETWORK")).get("NETID");
int i1=Integer.parseInt(s1);
int i2=Integer.parseInt(s2);
return i1-i2;
}
}
I'm stuck here to proceed further. Created one for Integer comparator, .Should I create for each dataType? also
I need to dynamically construct the composite comparator by parsing the "retunrParameters" , below sample is hard coded, how to create dynamically??
(String)((JSONObject) o1.get("NETWORK")).get("NETID"); -> this should be dynamically framed , since "returnParameters" are also dynamic in nature.[NETWORK & NETID may not be come in another request],so my comparator should be capable enough to frame the keys in runtime
Would anyone able to assist me to create composite comparator in runtime for sorting?
NOTE:- Java Pojo cannot be created as the response is dynamic nature
In your case a simple comparator that's provided with the sort parameters might be easier to understand than a bunch of nested comparators.
Basically you'd do something like this:
class ReturnParameterComparator implements Comparator<JSONObject> {
private List<ReturnParameter> params; //set via constructor
public int compare( JSONObject left, JSONObject right) {
int result = 0;
for( ReturnParameter p : params ) {
//how exactly you get those values depends on the actual structure of your data and parameters
String leftValueStr = left.get( p );
String rightValueStr = right.get( p );
switch( p.datatype ) {
case "String":
result = String.compare( leftValueStr, rightValueStr );
break;
case "int":
//convert and then compare - I'll leave the rest for you
}
//invert the result if the order is descending
if( "desc".equals(p.order ) {
result += -1;
}
//the values are not equal so return the order, otherwise continue with the next parameter
if( result != 0 ) {
return result;
}
}
//at this point all values are to be considered equal, otherwise we'd have returned already (from the loop body)
return 0;
}
}
Note that this is just a stub to get you started. You'll need to add quite a few things:
how to correctly use the parameters to extract the values from the json objects
how to convert the data based on the type
how to handle nulls, missing or incompatible data (e.g. if a value should be sorted as "int" but it can't be parsed)
Adding all those would be way too much for the scope of this question and depends on your data and requirements anyway.
EDITED after additional questions in comments and additional info in description
You have a couple of steps you need to do here to get to the solution:
You want to have the sorting be dynamic based on the value of the property sequence in the request. So you need to parse the names of those returnParameters and put them in order. Below I map them to a List where each String[] has the name and order (asc/desc). The list will be ordered using the value of sequence:
List<String[]> sortParams = params.stream() // params is a List<JSONObject>
.filter(json -> json.containsKey("sequence")) // filter those that have "sequence" attribute
.sorted( sequence ) // sorting using Comparator called sequence
.map(jsonObj -> new String[]{jsonObj.get("name").toString(), jsonObj.get("order").toString()} )
.collect(Collectors.toList());
Before this you'll map the objects in the returnParameters array in the request to a List first.Then the stream is processed by 1. filtering the JSONObjects to only keep those that have prop sequence, 2. sorting the JSONObjects using comparator below. 3. from each JSONObject get "name" & "order" and put them in a String[], 4. generate a list with those Arrays. This list will be ordered in the order of attributes with priority 1 first, then priority 2, etc, so it will be ordered in the same way you want the JSONObjects ordered in the end.
Comparator<JSONObject> sequence = Comparator.comparingInt(
jsonObj -> Integer.valueOf( jsonObj.get("sequence").toString() )
);
So for your example, sortParams would look like: List( String[]{"NETWORK/NETID", "asc"}, String[]{""INFODATA/NAME", "asc"}, String[]{"SOURCE/SYSTEM", "asc"} )
Then you need to write a method that takes two params: a JSONObject and a String (the path to the property) and returns the value of that property. Originally I advised you to use JSONAware interface and then figure out the sub-class, but let's forget about that for now.
I am not going to write this method for you. Just keep in mind that .get(key) method of JSON.Simple always yields an Object. Write a method with this signature:
public String findSortValue(JSONObject doc, String path){
// split the path
// find the parent
// cast it (parent was returned as an Object of type Object)
// find the child
return value;
}
Write a generic individual comparator (that compares values of just one sort attribute at a time) and figures out if it's an Int, Date or regular String. I would write this as a regular method so it'll be easier to combine everything later on. Since you had so many questions about this I've made an example:
int individualComparator(String s1, String s2){
int compResult = 0;
try{
int numeric1 = Integer.parseInt(s1);
int numeric2 = Integer.parseInt(s2);
compResult = numeric1 - numeric2; // if this point was reached both values could be parsed
} catch (NumberFormatException nfe){
// if the catch block is reached they weren't numeric
try{
DateTime date1 = DateTime.parse(s1);
DateTime date2 = DateTime.parse(s2);
compResult = date1.compareTo(date2); // compareTo method of joda.time, the library I'm using
} catch (IllegalArgumentException iae){
//if this catch block is reached they weren't dates either
compResult = s1.compareTo(s2);
}
}
return compResult;
};
Write an overall Comparator that combines everything
Comparator<JSONObject> overAllComparator = (jsonObj1, jsonObj2) -> {
List<String[]> sortValuesList = sortParams.stream()
.map(path -> new String[]{ findValueByName(jsonObj1, path), findValueByName(jsonObj2, path) } )
.collect(Collectors.toList());
//assuming we always have 3 attributes to sort on
int comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]);
int comp2 = individualComparator(sortValuesList.get(1)[0], sortValuesList.get(1)[1]);
int comp3 = individualComparator(sortValuesList.get(2)[0], sortValuesList.get(2)[1]);
int result = 0;
if (comp1 != 0){
result = comp1;
} else if (comp2 != 0){
result = comp2;
} else{
result = comp3;
}
return result;
};
This Comparator is written lambda-style, for more info https://www.mkyong.com/java8/java-8-lambda-comparator-example/ .
First it takes the ordered list of sortParams we made in step 1 and for each returns an array where position 0 has the value for jsonObj1, and position 1 has the value for jsonObj2 and collects it in sortValuesList. Then for each attribute to sort on, it get the result of the individualComparatormethod. Then it goes down the line and returns as result of the overall comparison the first one that doesn't result in 0 (when a comparator results in 0 both values are equal).
The only thing that's missing now is the asc/desc value from the request. You can add that by chainingint comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]); with a simple method that takes an int & a String and multiplies the int by -1 if the String equals "desc". (Remember that in sortParams we added the value for order on position 1 of the array).
Because the first list we made, sortParams was ordered based on the priority indicated in the request, and we always did evertything in the order of this list, the result is a multi-sort in this order. It is generic & will be determined dynamically by the contents of returnParams in the request. You can apply it to your list of JSONObjects by using Collections.sort()
My suggestion: learn about:
Comparator.comparing which allows you to build your comparator by specifying the key extractor
Comparator.thanComparing which allows you to chain multiple comparators. The comparators later in the chain are called only if predecessors say the objects are equal
A tutorial if you need one: https://www.baeldung.com/java-8-comparator-comparing

Refactor multiple If' statements in Java-8

I need to validate mandatory fields in my class
For example, 9 fields must not be null.
I need to check if they are all null but I am using multiple if statements for this now as below:
StringBuilder mandatoryExcessFields = new StringBuilder(MANDATORY_EXCESS_FIELDS.length);
if(Objects.isNull(excess.getAsOfDate())){
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[0]);
}
if(StringUtils.isEmpty(excess.getStatus())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[1]);
}
if(Objects.isNull(excess.getLimit())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[2]);
}
if(!Objects.isNull(excess.getLimit()) && Objects.isNull(excess.getLimit().getId())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[3]);
}
if(!Objects.isNull(excess.getLimit()) && Objects.isNull(excess.getLimit().getAsOfDate())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[4]);
}
if(Objects.isNull(excess.getExposure())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[5]);
}
if(!Objects.isNull(excess.getExposure()) && Objects.isNull(excess.getExposure().getCoordinates())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[6]);
}
if(!Objects.isNull(excess.getExposure()) && Objects.isNull(excess.getExposure().getValue())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[7]);
}
if(StringUtils.isEmpty(excess.getLimitValue())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[8]);
}
Do we have a better approach to reduce this boilerplate code or any design pattern or any new feature from Java-8 which I can leverage?
All the Object.isNull might be replaced with Optional object and its methods. Let's take example the line:
if (!Objects.isNull(excess.getLimit()) && Objects.isNull(excess.getLimit().getId())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[3]);
}
Would be simplified to (and squeezed on 1 line remains readable):
Optional.ofNullable(excess.getLimit()) // check the Limit
.map(limit -> limit.getId()) // if not null, getId
.ifPresent(i -> builder.append(MANDATORY_EXCESS_FIELDS[3])); // Append if present
And for the String.isEmpty(s) check, you have to create Optional in this way:
Optional.ofNullable(excess.getStatus()).filter(s -> !StringUtils.isEmpty(s))
A short way would be to pass those Optional object into the map and use the index to iterate through them and perform an action. int count is a number of checkings:
Map<Integer, Optional<?>> map = new HashMap<>();
map.put(...);
map.put(1, Optional.ofNullable(excess.getStatus()).filter(s -> !StringUtils.isEmpty(s)));
map.put(...);
map.put(3, Optional.ofNullable(excess.getLimit()).map(limit -> limit.getId()));
map.put(...);
for (int index=0; index<count; index++) {
map.get(index).ifPresent(any -> mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[index]));
}
And the for-cycle might be simplified as well:
IntStream.range(0, count).forEach(index ->
map.get(index)
.ifPresent(any -> mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[index])));
Basically, there are two ways here:
As suggested by the comment, NonNull as offered by Project Lombok for example
Java bean validation
I would heavily recommend to look into bean validation:
Define your classes that carry information as beans. And then use the wide range of annotations to mark the corresponding fields. And then use some existing framework to do the validation for you. You can even define your own annotations there, that run your own code.
You can use javax.validator and hibernate.validator with #NotNull annotation on each field (or whichever field you want) on your excess POJO class. This combination provides an extensive pattern checking as well.
By this you don't have to do all the if checks explicitly. You can get ride of not only null checks but also pattern matching checks which can get scattered all over your code.
Basically the initialisation and assignments should not set any field to null.
If this is unopportune (a field being really logically optional), the field should probably be an Optional<...>, assigned with an Optional.ofNullable(...). This ensures that at usage the field is safely processed, but causes editing work of course.
Seeing the code now, here it seems that there is no easy refactoring.
The code could be refactored; somewhere a mapping of features is missing.
Predicate<Excess>[] parts = {
exc -> Objects.isNull(exc.getAsOfDate()),
exc -> StringUtils.isEmpty(exc.getStatus()),
...
};
for (int i = 0; i < parts.length; ++i) {
if (parts[i].test(excess)) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[i]);
}
}
Or such.
As easy refactoring you could introduce two helper methods :
private String createErrorMsgIfObjectNull(Object o, String errorMessage) {
return Objects.isNull(o) ? errorMessage : "";
}
private String createErrorMsgIfStringEmpty(String s, String errorMessage) {
return StringUtils.isEmpty(s) ? errorMessage : "";
}
And use them in this way :
StringBuilder mandatoryExcessFields = new StringBuilder(MANDATORY_EXCESS_FIELDS.length);
mandatoryExcessFields.append(createErrorMsgIfObjectNull(excess.getAsOfDate(), MANDATORY_EXCESS_FIELDS[0]))
.append(createErrorMsgIfStringEmpty(excess.getStatus(), MANDATORY_EXCESS_FIELDS[1]))
.append(createErrorMsgIfObjectNull(excess.getLimit(), MANDATORY_EXCESS_FIELDS[2]))
// ...
By checking the type of the object to test you could still go further. You would have a single helper method that will apply the processing according to the argument type :
private String createErrorMsgIfNullOrEmptyString(Object o, String errorMessage) {
if (o instanceof String) {
return StringUtils.isEmpty((String)o) ? errorMessage : "";
}
return Objects.isNull(o) ? errorMessage : "";
}
A Java 8 stream way would inline the helper in a filter and map() operations and would collect the String result :
List<SimpleImmutableEntry<Object, String>> objectAndErrorMessageList = new ArrayList<>();
int i = 0;
objectAndErrorMessageList.add(new SimpleImmutableEntry<>(excess.getAsOfDate(), MANDATORY_EXCESS_FIELDS[i++]));
objectAndErrorMessageList.add(new SimpleImmutableEntry<>(excess.getStatus(), MANDATORY_EXCESS_FIELDS[i++]));
// and so for
String globalErrorMsg =
objectAndErrorMessageList.stream()
.filter(e -> {
Object objectToValid = e.getKey();
if (objectToValid == null) {
return true;
}
if (objectToValid instanceof String && StringUtils.isEmpty(objectToValid)) {
return true;
}
return false;
})
.map(SimpleImmutableEntry::getValue)
.collect(Collectors.joining(""));
Other solution would be like this: same as #Nikolas answer.
Map<Integer, Predicate<Excess>> map = new HashMap<>();
Predicate<Excess> checkStatus = excess -> excess.getStatus().isEmpty();
Predicate<Excess> checkLimit = excess -> Objects.isNull(excess.getLimit());
Predicate<Excess> checkLimitId = excess -> Objects.isNull(excess.getLimit().getId());
Predicate<Excess> checkLimitAndId = checkLimit.and(checkLimitId);
// other predicates
map.put(1,checkStatus);
map.put(2,checkLimit);
map.put(3,checkLimitAndId);
// put other predicates ...
for (Map.Entry<Integer, Predicate<Excess>> m : map.entrySet()) {
if (m.getValue().test(excess)) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[m.getKey()]);
}
}
A little bit complicated, but I have a good solution because it's generic and can be used with any objects:
Excess excess = new Excess(new Limit());
Checker<Excess, Excess> checker = new Checker<>(
identity(),
List.of(
new CheckerValue<>("excess date is null", Excess::getAsOfDate),
new CheckerValue<>("limit is null", Excess::getLimit)
),
List.of(new Checker<>(Excess::getLimit, List.of(new CheckerValue<>("limit id is null", Limit::getId))))
);
System.out.println(checker.validate(excess));
This code will print:
excess date is null
limit id is null
The first class Checker contains:
sourceFunction - for getting the object
values - for checking each field from object obtained from sourceFunction
children - a list of Checker
class Checker<S, T> {
Function<S, T> sourceFunction;
List<CheckerValue<T>> values;
List<Checker<T, ?>> children = emptyList();
/*All args constructor; 2 args constructor*/
public String validate(S object) {
T value = sourceFunction.apply(object);
if(value != null) {
String valueString = values.stream().map(v -> v.validate(value)).filter(Optional::isPresent).map(Optional::get).collect(joining("\n"));
valueString += "\n\t";
valueString += children.stream().map(c -> c.validate(value)).collect(Collectors.joining("\n"));
return valueString;
}
return "";
}
}
and CheckerValue class:
class CheckerValue<T> {
String validationString;
Function<T, Object> fun;
/*all args constructor*/
public Optional<String> validate(T object) {
return fun.apply(object) != null ? Optional.empty() : Optional.of(validationString);
}
}

Java class toString() and back from string? [duplicate]

This question already has answers here:
Generate Java Object from toString representation [duplicate]
(3 answers)
Closed 5 years ago.
I've created some class with custom toString() function:
public class Test {
public String eventName;
public Long eventTime; //timestamp
public Integer firstEventResult;
public Integer secondEventResult;
// ... and dozens more variables
#Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder("Event(");
stringBuilder.append("name:");
stringBuilder.append(this.eventName);
stringBuilder.append(", ");
stringBuilder.append("time:");
stringBuilder.append(this.eventTime);
if(firstEventResult != null) {
stringBuilder.append(", ");
stringBuilder.append("firstEventResult:");
stringBuilder.append(this.firstEventResult);
}
if(secondEventResult != null) {
stringBuilder.append(", ");
stringBuilder.append("secondEventResult:");
stringBuilder.append(this.secondEventResult);
}
// ...
stringBuilder.append(")");
return stringBuilder.toString();
}
}
and that toString() function provides me string like that:
Event(name:Test, time:123456789, firstEventResult:200)
How can I in this case convert back above string to the class again?
If you really insist on using a custom serialization with .toString() over using a Standard serializer like GSON, then you need to write your own serializer/parser to deserialize your object, to do so you have two options here:
Option 1:
In general cases you will need to use ObjectInputStream class to get back the objects from the string using its readObject() and other relevant methods.
These are the main steps to follow:
Serialize to byte array.
Convert to Base64.
Decode Base64 back to byte array
And finally deserialize.
You can follow this OBJECT STREAMS – SERIALIZATION AND DESERIALIZATION IN JAVA EXAMPLE USING SERIALIZABLE INTERFACE article for further details.
Option 2:
Otherwise you can extract the object members using either Regex or String manipulations,for example if you know that the the format is exactly like this:
Event(name:Test, time:123456789, firstEventResult:200)
Starts with Event(
values separated with ,
ends with )
Then you could do the following:
Convert the value pairs into a Map
Create a new instance of this class and set the values from a map
public static YourClassName fromString( String str ) {
YourClassName result = new YourClassName();
// remove the start and ending ( not tested :P )
String trimmed = str.substring( 6, str.length - 7 );
String[] valuePairs = trimmed.split( ", " );
Map<String, String> values = new HashMap<>();
// convert value pairs into a map
for ( String valuePair : valuePairs ) {
String[] pair = valuePair.split( ":" );
String key = pair[0];
String value = pair[1];
values.put( key, value );
}
// set the values one by one
if ( values.get( "name" ) != null ) result.name = values.get( "name" );
if ( values.get( "firstEventResult" ) != null ) result.firstEventResult = Integer.parse( values.get( "firstEventResult" ) );
// and all others...
return result;
}

Parsing differently named elements as a single list using SIMPLE XML library for android

I was wondering if there was anyway to treat col_1,col_2...etc as a list rather than separate elements, using the SIMPLE XML Library for Android. I have been reading a bit about substitutions but I'm still confused.
Current Format :
<box-headers
dataTable="boxscore"
col_1="FINAL"
col_2="1"
col_3="2"
col_4="3"
col_5="4"
col_6="5"
col_7="6"
col_8="7"
col_9="8"
col_10="9"
col_11="R"
col_12="H"
col_13="E">
table
</box-headers>
I want to be able to parse out the col's as a list of some sort so I can handle any number of cols. Is this possible?
As ng said before: Use a Converter for this. Simple is brilliant in letting you customize every step of processing (while on the other hand it's possible to let you (de-)serialize even complex structures with some lines of code).
So here's an example:
A Class that will hold the values from the list:
#Root(name = "example")
#Convert(value = ListConverter.class) // Specify the Converter that's used for this class
public class Example
{
// This element will be set with the values from 'box-headers' element
#ElementList(name = "box-headers")
private List<String> values;
// This constructor is used to set the values while de-serializing
// You can also use setters instead
Example(List<String> values)
{
this.values = values;
}
//...
}
The Converter:
public class ExampleConverter implements Converter<Example>
{
#Override
public Example read(InputNode node) throws Exception
{
List<String> list = new ArrayList<>(); // List to insert the 'col_' values
NodeMap<InputNode> attributes = node.getAttributes(); // All attributes of the node
Iterator<String> itr = attributes.iterator();
while( itr.hasNext() ) // Iterate over all attributes
{
final String name = itr.next(); // The name of the attribute
if( name.startsWith("col_") ) // Check if it is a 'col' attribute
{
// Insert the value of the 'col_' attribute
list.add(attributes.get(name).getValue());
}
}
// Return the result - instead of a constructor you can use setter(s) too
return new Example(list);
}
#Override
public void write(OutputNode node, Example value) throws Exception
{
// TODO: Implement serializing here - only required if you want to serialize too
}
}
How to use:
// Serializer, don't forget `AnnotationStrategy` - without it wont work
Serializer ser = new Persister(new AnnotationStrategy());
// Deserialize the object - here the XML is readen from a file, other sources are possible
Example ex = ser.read(Example.class, new File("test.xml"));
This example uses only col_xy attributes, everything else is dropped. If you need those values too it's easy to implement them. You only have to retrieve them from the InputNode and set them into your output.

Categories