Easiest way to extract fields from JSON - java

Update: I should have mentioned this right off the bat: I first considered a Java/JSON mapping framework, but my manager does not want me adding any more dependencies to the project, so that is out as an option. The JSON-Java jar is already on our classpath, so I could use that, but still not seeing the forest through the trees on how it could be used.
My Java program is being handed JSON of the following form (althought the values will change all the time):
{"order":{"booze":"1","handled":"0","credits":"0.6",
"execute":0,"available":["299258"],"approved":[],
"blizzard":"143030","reviewable":["930932","283982","782821"],
"units":"6","pending":["298233","329449"],"hobbit":"blasphemy"}}
I'm looking for the easiest, efficient, surefire way of cherry-picking specific values out of this JSON string and aggregating them into a List<Long>.
Specifically, I'm looking to extract-and-aggregate all of the "ids", that is, all the numeric values that you see for the available, approved, reviewable and pending fields. Each of these fields is an array of 0+ "ids". So, in the example above, we see the following breakdown of ids:
available: has 1 id (299258)
approved: has 0 ids
reviewable: has 3 ids (930932, 283982, 782821)
pending: has 2 ids (298233, 329449)
I need some Java code to run and produce a List<Long> with all 6 of these extracted ids, in no particular order. The ids just need to make it into the list.
This feels like an incredibly complex, convoluded regex, and I'm not even sure where too begin. Any help at all is enormously appreciated. Thanks in advance.

The easiest way IMO is use a json library such as gson, jackson, json.org, etc, parse de JSON into an object and create a new List<Long> with the values of the properties you need.
Pseudocode with gson:
class Order {
long[] available;
long[] approved;
...
}
Order order = gson.fromJson("{ your json goes here }", Order.class);
List<Long> result = new ArrayList<Long>();
result.add(order.getAvailable());
result.add(order.getApproved());
...
Pseudocode with json.org/java:
JSONObject myobject = new JSONObject("{ your json goes here"});
JSONObject order = myobject.getJSONObject("order");
List<Long> result = new ArrayList<Long>();
for (int i=0; i<order.getJSONArray("approved").length(); i++) {
Long value = order.getJSONArray("approved").getLong(i);
result.add(value);
}
...

Related

Pojo Conversion with HibernateMapping & Jaxb

I have two Pojo classes, one for hibernate mapping and one for jaxb.
So I get a List<CarA> using Criteria.list() and I would like to transform it into a List<CarB> in order to send it back to my client as a SOAP message.
Would you have any idea how to do the conversion from CarA to CarB?
My list contains more than 100 elements and the construction gets expensive very fast.
I wish I didn't have to build a list from A to Z.
Thanks for your help
You can use model mapper library : https://www.baeldung.com/java-modelmapper
Example of what you can do:
List<Carb> lb = new ArrayList();
for (Cara a : la )
lb.add(mapper.map(a, Carb.class))

how to use mongodb java-driver Projections.slice

I am trying to use Aggregates.project to slice the array in my documents.
My documents is like
{
"date":"",
"stype_0":[1,2,3,4]
}
in the mongochef looks like
and my code in java is :
Aggregates.project(Projections.fields(
Projections.slice("stype_0", pst-1, pen-pst),Projections.slice("stype_1", pst-1, pen-pst),
Projections.slice("stype_2", pst-1, pen-pst),Projections.slice("stype_3", pst-1, pen-pst))))
finally i get error
First argument to $slice must be an array, but is of type: int
I guess that is because the first element in stype_0 is int , but I really do not know why? Thanks a lot!
Slice has two versions. $slice(aggregation) & $slice(projection). You are using the wrong one.
Aggregate slice function doesn't have any built-in support. Below is an example for one such projection. Do the same for all the other projection fields.
List stype_0 = Arrays.asList("$stype_0", 1, 1);
Bson project = Aggregates.project(Projections.fields(new Document("stype_0", new Document("$slice", stype_0))));
AggregateIterable<Document> iterable = dbCollection.aggregate(Arrays.asList(project));

ElasticSearch Java API to query and return single column instead of the whole json document

While searching using java api in elaticsearch, I would like to retrieve only one column.
Currently when I query using the Java API it returns the whole record like this: [{_id=123-456-7890, name=Wonder Woman, gender=FEMALE}, {_id=777-990-7890, name=Cat Woman, gender=FEMALE}]
The record above correctly matches the search condition shown in th . As shown in the code below:
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
SearchRequestBuilder srb = client.prepareSearch("heros")
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
MatchQueryBuilder mqb;
mqb = QueryBuilders.matchQuery("name", "Woman");
srb.setQuery(mqb);
SearchResponse response = srb.execute().actionGet();
long totalHitCount = response.getHits().getTotalHits();
System.out.println(response.getHits().getTotalHits());
for (SearchHit hit : response.getHits()) {
result.add(hit.getSource());
}
System.out.println(result);
I want only one column to be returned. If I search for name I just want the full names back in a list: "Wonder Woman", "Cat Woman" only not the whole json record for each of them. If you think I need to iterate over the result list of maps in java please propose an example of how to do that in this case.
You can specify the fields to be returned from a search, per documentation. This can be set via SearchRequestBuilder.addFields(String... fields), ie:
SearchRequestBuilder srb = client.prepareSearch("heros")
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
.addFields("name");
Better combine both:
use .addFields("name") to tell ES that it needs to return only this
column
use hit.field("name").getValue().toString() to get the result
It is important to use .addFields when you don't need the whole document, but the specific field/s as it will lower the overhead and the network traffic
I figured it out.
List<String> valuesList= new ArrayList<String>();
for (SearchHit hit : response.getHits()) {
result.add(hit.getSource());
valuesList.add(hit.getSource().get("name").toString());
}
The other solutions didn't work for me, hit.getSource() was returning null. Maybe they are deprecated? Not sure. But here was my solution, which FYI can speed things up considerably if you are only getting one field and you are getting lots of results.
Use addFields(Strings) on your SearchRequestBuilder as mentioned, but then when you are getting the values you need to use:
hit.getFields().get( fieldName ).getValue()
or
hit.getFields().get( fieldName ).getValues()
to get a single value or a list of values depending on the field.

Faceting using SolrJ and Solr4

I've gone through the related questions on this site but haven't found a relevant solution.
When querying my Solr4 index using an HTTP request of the form
&facet=true&facet.field=country
The response contains all the different countries along with counts per country.
How can I get this information using SolrJ?
I have tried the following but it only returns total counts across all countries, not per country:
solrQuery.setFacet(true);
solrQuery.addFacetField("country");
The following does seem to work, but I do not want to have to explicitly set all the groupings beforehand:
solrQuery.addFacetQuery("country:usa");
solrQuery.addFacetQuery("country:canada");
Secondly, I'm not sure how to extract the facet data from the QueryResponse object.
So two questions:
1) Using SolrJ how can I facet on a field and return the groupings without explicitly specifying the groups?
2) Using SolrJ how can I extract the facet data from the QueryResponse object?
Thanks.
Update:
I also tried something similar to Sergey's response (below).
List<FacetField> ffList = resp.getFacetFields();
log.info("size of ffList:" + ffList.size());
for(FacetField ff : ffList){
String ffname = ff.getName();
int ffcount = ff.getValueCount();
log.info("ffname:" + ffname + "|ffcount:" + ffcount);
}
The above code shows ffList with size=1 and the loop goes through 1 iteration. In the output ffname="country" and ffcount is the total number of rows that match the original query.
There is no per-country breakdown here.
I should mention that on the same solrQuery object I am also calling addField and addFilterQuery. Not sure if this impacts faceting:
solrQuery.addField("user-name");
solrQuery.addField("user-bio");
solrQuery.addField("country");
solrQuery.addFilterQuery("user-bio:" + "(Apple OR Google OR Facebook)");
Update 2:
I think I got it, again based on what Sergey said below. I extracted the List object using FacetField.getValues().
List<FacetField> fflist = resp.getFacetFields();
for(FacetField ff : fflist){
String ffname = ff.getName();
int ffcount = ff.getValueCount();
List<Count> counts = ff.getValues();
for(Count c : counts){
String facetLabel = c.getName();
long facetCount = c.getCount();
}
}
In the above code the label variable matches each facet group and count is the corresponding count for that grouping.
Actually you need only to set facet field and facet will be activated (check SolrJ source code):
solrQuery.addFacetField("country");
Where did you look for facet information? It must be in QueryResponse.getFacetFields (getValues.getCount)
In the solr Response you should use QueryResponse.getFacetFields() to get List of FacetFields among which figure "country". so "country" is idenditfied by QueryResponse.getFacetFields().get(0)
you iterate then over it to get List of Count objects using
QueryResponse.getFacetFields().get(0).getValues().get(i)
and get value name of facet using QueryResponse.getFacetFields().get(0).getValues().get(i).getName()
and the corresponding weight using
QueryResponse.getFacetFields().get(0).getValues().get(i).getCount()

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