java: convert HashMap with dynamic keys to Bean - java

I'm trying to convert a large Map> to some JavaBean. The key of map corresponds to some property of JavaBean, and the value somehow is decoded to property value. So I decided to use some util for that, but don't know what will work. There are some requirements I have to this util (or framework):
all configuration must be in separate files
should be a possibility to map dynamic quantity of keys:
there is a map:
key | value
quan | n
key_1| value_1
key_2| value_2
........ | .............
key_n| value_n
where n - is any number
and the JavaBean has a List of some beans. They have a property. value_1, value_2, ... must be mapped in this property, and in the end there must be so much beans in list, as these keys and values in map.
3.. should be a possibility to set up custom decoder for property mapping, because in most cases the value in map is a List with 1 value, so I need to get the first item of list (if it's not empty).
4.. should be a possibility run some script to execute extraordinary mappings, for example:
there is a map, that is described in 2d point.
and the JavaBean has a property of type HashMap, where value_1 is mapped to Bean1 and some analogous value from input map is mapped to Bean2.
I've tried to use smooks, but when I've started, all these requirements were not clear yet and the smooks was something new, I haven't worked with it until now. So the smooks config doesn't contain the whole business-logic (because of second req.) and looks ugly, I don't like that. I can show the most ugliest fragment for 2d point:
<jb:bean beanId="javaBean" class="com.example.JavaBean" createOnElement="map">
<jb:wiring property="someBeans" beanIdRef="someBeanItems"/>
</jb:bean>
<jb:bean beanId="someBeanItems" class="java.util.ArrayList" createOnElement="map/entry">
<jb:wiring beanIdRef="someBeanItem"/>
</jb:bean>
<jb:bean beanId="someBeanItem" class="com.example.someBeanItem" createOnElement="map/entry">
<condition>map.quan[0]>0</condition>
<jb:expression property="property1">
index = map.quan[0]-1;
value = additionalProperties.property1_List[index];
map.quan[0] = map.quan[0] - 1;
return value;
</jb:expression>
</jb:bean>
Here "property1_List" is builded before executing smooks.
Now I look for something more nice and need your help: maybe you know how to make that better using smooks? Or what another frameworks for mapping can you recommend for my issue?

Related

How to set defined key as example in generated documentation of a hashmap in springdoc?

I am documenting a java API but the keys for properties that are maps or associative arrays are represented as property1, property2, property3 ... etc.
this is an example of that
#Schema(
description = " This object contains the plans selected by the user.",
name = "plans")
var plans: Map<Int, Plan> = ConcurrentHashMap()
the representation of the generated example in the UI is good in terms of content but I would like to replace just the generated keys by the real ones.
ie property1 by 258.
can someone helpme on how can I get this done using springdoc and its annotations ?

Updating transaction in Datomic for an attribute that has many cardinality

I have searched for two days and haven't seen any code that is closed to this. This is the only code in java that I seen and it's not exactly what I wanted.
conn.transact(list(list("db.fn/cas", datomic_id, "attribute you want to update", old value, new value))).get();
I have tried this code with a single value in the old value and a single value in the new value but it just stack the information instead of overlaying it.
Example: old value is chicken and new value is fish. After the transaction, it's [chicken, fish] instead of what I expected to be just [fish] and chicken will be move into archive(history).
So the question is, how do you ref the old array value and how do you give the new value an array so it'll update as what I expected to be as stated above.
I remember reading somewhere that under the hood it's just a series of values linking to one attribute. If this is the case does that mean that I have to find the datomic id of the string and change it? Also have to remove it if it's not on the new list?
FYI, these are the generic transaction functions I currently use for this kind of task (declared from Clojure, but should be fairly easy to adapt to Java if required):
[{:db/ident :bsu.fns/replace-to-many-scalars,
:db/doc "Given an entity's lookup ref, a to-many (scalar) attribute, and a list of new values,
yields a transaction that replaces the old values by new ones"
:db/id (d/tempid :db.part/user),
:db/fn (d/function
'{:lang :clojure,
:imports [],
:requires [[datomic.api :as d]],
:params [db entid attr new-vals],
:code (let [old-vals (if-let [e (d/entity db entid)] (get e attr) ())
to-remove (remove (set (seq new-vals)) old-vals)]
(concat
(for [ov to-remove] [:db/retract entid attr ov])
(for [nv new-vals] [:db/add entid attr nv]))
)}),
}
{:db/ident :bsu.fns/to-many-retract-all-but,
:db/doc "Given an entity lookup ref, a to-many (entity) attribute, and a list of lookup refs
expands to a transaction which will retract all the [origin `to-many-attr` target] relationships but those for which target is among the `to-spare-lookup-refs`"
:db/id (d/tempid :db.part/user),
:db/fn (d/function
'{:lang :clojure,
:imports [],
:requires [[datomic.api :as d]],
:params [db origin to-many-attr to-spare-lookup-refs],
:code (let [old-targets-ids (d/q '[:find [?t ...] :in $ ?to-many-attr ?origin :where [?origin ?to-many-attr ?t]]
db to-many-attr origin)
to-spare-ids (for [lr to-spare-lookup-refs] (:db/id (d/entity db lr)))
to-delete (->> old-targets-ids (remove (set to-spare-ids)))]
(for [eid to-delete] [:db/retract origin to-many-attr eid])
#_[old-targets-ids to-update-ids to-delete])}),
}]
I don't claim at all they're optimal performance or design-wise, but they've worked for me so far. HTH.
If you need a "last write wins" style consistent solution to replace all values for a particular entity for a card many attribute, your best bet is to go with a transaction function. You could take the following approach:
Get all datoms matching entity + attribute you want to retract all values for.
Generate retractions for all of them.
Create add transactions for all new values (e.g. from a passed collection)
Remove any conflicts (i.e. if you have the same EAV with both an add and a result)
Return the resulting transaction data.

Freemarker: iterating nested list in hash

I want to iterate a List nested in a Map, the data structure is like:
Map<Integer, List<Integer>> groups = new TreeMap<>()
// Some code else to put values into groups ...
Freemarker template:
<#list groups?keys as groupKey>
${groupKey} // It's OK here.
<#list groups[groupKey] as item> // Exception threw here, detail message is pasted below
${item}
</#list>
</#list>
Detail exception message:
FreeMarker template error:
For "...[...]" left-hand operand: Expected a sequence or string or something automatically convertible to string (number, date or boolean), but this evaluated to an extended_hash (wrapper: f.t.SimpleHash):
==> groups
So, what is the problem?
P.S.
I have tried groups.get(groupKey) instead of groups[groupKey], it throws a new Exception stack:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
java.lang.String.compareTo(String.java:108)
java.util.TreeMap.getEntry(TreeMap.java:346)
java.util.TreeMap.get(TreeMap.java:273)
freemarker.template.SimpleHash.get(SimpleHash.java:160)
freemarker.core.Dot._eval(Dot.java:40)
freemarker.core.Expression.eval(Expression.java:76)
The problem in the original question is that FTL's hash type is not like Map. It's a collection of "variables", that is, the keys must be String-s. (Even that ?keys works is a glitch in BeansWrapper... though now it comes handy.) Since the key is a number, FTL assumes that you want to get an item from a sequence (a List or array), or that you want to get a character from a string, hence the original error message.
The solution is using the Java API-s, like get in Dev-an's answer. (On the long term FTL meant to introduce the map type, so all this problems with non-string keys will end, but who knows when that will be...)
Update: Since 2.3.22 there's ?api to access the Java API of objects, like myMap?api.get(nonStringKey). However, it's by default not allowed (see the api_builtin_enabled configuration setting and more in the Manual: http://freemarker.org/docs/ref_builtins_expert.html#ref_buitin_api_and_has_api). Also note that as Java maps are particular about the numerical type, if the key is not an Integer coming from a Java, you have to use myMap?api.get(myNumericalKey?int).
Try the following:
<#list groups?keys as groupKey>
${groupKey}
<#list groups.get(groupKey) as item>
${item}
</#list>
</#list>

Converting a awk 2D array with counts into hashmap in java

I found this problem so interesting. I am using an awk 2D array that has a key,value,count of the same. and that is being printed to a file. This file is in the below format
A100|B100|3
A100|C100|2
A100|B100|5
Now I have a file like this .. My motive is to convert this file into a hash map so that the final output from the hash map is.
A100|B100|8
A100|C100|2
Just an aggregation
The challenge is, this one has 3 dimensions and not two. I did have an another file in the below format which is
D100|4
H100|5
D100|6
I easily aggregated the above as it is only 2D and I used the below code to do that
String[] fields= strLine.trim().split("\\|");
if(hashmap.containsKey(fields[0]))
{
//Update the value of the key here
hashmap.put(fields[0],hashmap.get(fields[0]) + Integer.parseInt(fields[1]));
}
else
{
//Inserting the key to the map
hashmap.put(fields[0],Integer.parseInt(fields[1]));
}
So this was quite simple for implementation.
But when it comes to 3D I have to have an another check inside.. My idea for this is to maintain a [B100,5(beanObject[5])]
Map<String,beanClassObject> hashmap=new Map<String,beanClassObject>();
secongField hash map which has been used in the code that has a mapping relation between the created ben Object subscript and the key as the second field "For instance it is "
This bean class would have the getter and setter method for the 2nd and 3rd fields of the file. I hope I am clear with this point. So the implementation of this would be
if(hashmap.containsKey(fields[0]))
{
**//Have to check whether the the particular key value pair already exists ... I dint find any method for this ... Just a normal iteration is there .. Could you ppl guide me regarding this**
//Update the value of the key here
secondFieldHashMap.get(fields[1]).COunt= secondFieldHashMap.get(fields[1]).getCOunt+ Integer.parseInt(fields[2]));
}
else
{
//Inserting the key to the map
hashmap.put(fields[0],Integer.parseInt(fields[1]));
secondFieldHashMap.get(fields[1]).COunt= Integer.parseInt(fields[2]));
}
else
{
// This meands there is no key field
// Hence insert the key field and also update the count of seconfFieldHashMap as done previously.
}
COuld you ppl please throw some ideas regarding this. Thank you
Consider using a Table available in the Google Guava libraries.

retrieving the values from the nested hashmap

I have a XML file with many copies of table node structure as below:
<databasetable TblID=”123” TblName=”Department1_mailbox”>
<SelectColumns>
<Slno>dept1_slno</Slno>
<To>dept1_to</To>
<From>dept1_from</From>
<Subject>dept1_sub</Subject>
<Body>dept1_body</Body>
<BCC>dept1_BCC</BCC>
<CC>dept1_CC</CC>
</SelectColumns>
<WhereCondition>MailSentStatus=’New’</WhereCondition>
<UpdateSuccess>
<MailSentStatus>’Yes’</MailSentStatus>
<MailSentFailedReason>’Mail Sent Successfully’</MailSentFailedReason>
</UpdateSuccess>
<UpdateFailure>
<MailSentStatus>’No’</MailSentStatus>
<MailSentFailedReason>’Mail Sending Failed ’</MailSentFailedReason>
</ UpdateFailure>
</databasetable>
As it is not an efficient manner to traverse the file for each time to fetch the details of each node for the queries in the program, I used the nested hashmap concept to store the details while traversing the XML file for the first time. The structure I used is as below:
MapMaster
Key Value
123 MapDetails
Key Value
TblName Department1_mailbox
SelectColumns mapSelect
Key Value
Slno dept1_slno
To dept1_to
From dept1_from
Subject dept1_sub
Body dept1_body
BCC dept1_BCC
CC dept1_CC
WhereCondition MailSentStatus=’New’
UpdateSuccess mapUS
MailSentStatus ’Yes’
MailSentFailedReason ’Mail Sent Successfully’
UpdateFailure mapUF
MailSentStatus ’No’
MailSentFailedReason ’Mail Sending Failed’
But the problem I’m facing now is regarding retrieving the Value part using the nested Keys. For example,
If I need the value of Slno Key, I have to specify TblID, SelectColumns, Slno in nested form like:
Stirng Slno = ((HashMap)((HashMap)mapMaster.get(“123”))mapDetails.get(“SelectColumns”))mapSelect.get(“Slno”);
This is unconvinent to use in a program. Please suggest a solution but don’t tell that iterators are available. As I’ve to fetch the individual value from the map according to the need of my program.
EDIT:my program has to fetch the IDs of the department for which there is privilege to send mails and then these IDs are compared with the IDs in XML file. Only information of those IDs are fetched from XML which returned true in comparison. This is all my program. Please help.
Thanks in advance,
Vishu
Never cast to specific Map implementation. Better use casting to Map interface, i.e.
((Map)one.get("foo")).get("bar")
Do not use casting in your case. You can define collection using generics, so compiler will do work for you:
Map<String, Map> one = new HashMap<String, Map>();
Map<String, Integer> two = new HashMap<String, Integer>();
Now your can say:
int n = one.get("foo").get("bar");
No casting, no problems.
But the better solution is not to use nested tables at all. Create your custom classes like SelectColumns, WhereCondition etc. Each class should have appropriate private fields, getters and setters. Now parse your XML creating instance of these classes. And then use getters to traverse the data structure.
BTW if you wish to use JAXB you do not have to do almost anything! Something like the following:
Unmarshaller u = JAXBContext.newInstance(SelectColumns.class, WhereCondition.class).createUnmarshaller();
SelectColumns[] columns = (SelectColumns[])u.unmarshal(in);
One approach to take would be to generate fully qualified keys that contain the XML path to the element or attribute. These keys would be unique, stored in a single hashmap and get you to the element quickly.
Your code would simply have to generate a unique textual representation of the path and store and retrieve the xml element based on the key.

Categories