Nested loop in birt report - java

I'm using Eclipse Birt to generate report from a JSON File.
My JSON file look like this :
{
"cells":[
{
"type":"basic.Sensor",
"custom":{
"identifier":[
{
"name":"Name1",
"URI":"Value1"
},
{
"name":"Name4",
"URI":"Value4"
}
],
"classifier":[
{
"name":"Name2",
"URI":"Value2"
}
],
"output":[
{
"name":"Name3",
"URI":"Value3"
}
],
},
"image":{
"width":50,
"height":50,
"xlink:href":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAABEJAAARCQBQGfEVAAAABl0RVh0U29mdHdhcmUAd3Vi8f+k/EREURQtsda2Or/+nFLqP6T5Ecdi0aJFL85msz2Qxyf4JIumMAx/ClmWt23GmL1kO54CXANAVH+WiN4Sx7EoNVkU3Z41BDHMeXAxjvOxNr7RJjzHX7S/jAflwBxkJr/RwiOpWZ883Nzd+Wpld7tkBr/SJr7ZHZbHZeuVweSnPfniocMAWYwcGBafH0OoPamFGAaY4ZBZjmmFGAaY4ZBZjmmFGAaY4ZBZjmmFGAaY7/B94QnX08zxKLAAAAAElFTkSuQmCC"
}
}
},
{
"type":"basic.Sensor",
"custom":{
"identifier":[
{
"name":"Name1",
"URI":"Value1"
},
{
"name":"Name4",
"URI":"Value4"
}
],
"classifier":[
{
"name":"Name2",
"URI":"Value2"
}
],
"output":[
{
"name":"Name3",
"URI":"Value3"
}
],
},
"image":{
"width":50,
"height":50,
"xlink:href":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9igAQAAAgAIoAEAAAIACKABAAACAAigAQAAAgAIoAEAAAIACKABAAACAAigAQAAAgAIoAEAAAIADqhvprADeSsau00l5NAAAAAElFTkSuQmCC"
}
}
},
{
"type":"basic.Platform",
"custom":{
"identifier":[
{
"name":"Name1",
"URI":"Value1"
}
],
"classifier":[
{
"name":"Name2",
"URI":"Value2"
}
],
"output":[
{
"name":"Name3",
"URI":"Value3"
}
],
"image":{
"width":50,
"height":50,
"xlink:href":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAA6TH0jqtg6U8EsCdnm3SpevSK7Pb85xABEMBuLAn2hxjRve7SFzYEaB/HhytLQ4ABRwCWBPvBKnRk6U8EkBeOD9f7iwAGHAGEYEmwDxLvzNKfCCDP8NGLQd3lY7D0JwI4kmlwfHhX6dTSXxsRAAHsWR7aUjc7uM5Wg=="
}
}
}
]
}
i have 3 cells and each one contains 1 Image 1 Name 1 Type and 3 Tables , this is what i have done so far :
what i'm struggling to do is a nested loop, i want to have for each object( Cell) in my JSON a paragraph numerated like this :
2.x Component cell's Name :
Image
Output Table
Identifier Table
Classifier Table
So to do this i need to itterate on each cell and then itterate on each table Output, identifier and classifier and i have no idea how can i do this, a nested loop. like a List which represent the number of cells, that contains 3 tables, one image , one name.
**Edit : **
this is the open method for the data set
// Grab the JSON file and place it in a string
fisTargetFile = new FileInputStream(new File("C:/Users/Sample Reports/moe.json"));
input = IOUtils.toString(fisTargetFile, "UTF-8");
// Store the contents in a variable
jsonData = input;
// Convert the String to a JSON object
myJSONObject = eval( '(' + jsonData + ' )' );
// Get the length of the object
len = myJSONObject.cells.length;
// Counter
count = 0;
Fetch method :
if(count < len) {
var name = myJSONObject.cells[count].attrs.text["text"];
var type = myJSONObject.cells[count].type;
var icon =myJSONObject.cells[count].attrs.image["xlink:href"];
icon = icon.split(",");
icon= icon[1];
imageDataBytes = icon;
row["name"] = name;
row["type"] = type;
row["icon"] = Base64ToBlob.toBytes(icon);
Logger.getAnonymousLogger().info( row["icon"]);
count++;
return true;
}
return false;

You want to use nested tables, there is a good tutorial showing how to link nested tables to an outer table: please watch carefully this demo first, in particular see how the sub-table is linked to the outer table through a dataset parameter.
Of course your case is more challenging because you need to do this with scripted datasets and multiple sub-tables. I already did something similar, you have to create one scripted dataset for each sub-table. Key-points are:
In "parameters" section of each sub-dataset, create one input parameter and name it for instance "systemID"
Create your sub-tables by "drag & drop" each dataset within the outer table
In "bindings" section of each sub-table, link parameter "systemID" to the ID field of the outer table
In "open" event of sub-datasets, access the value of the parameter with this expression: inputParams["systemID"] Thus you can filter related rows in "myJSONObject".
It is important to make sure "myJSONObject" is initialized once for all, otherwise performances could dramatically decrease if it is evaluated on each iteration. For example evaluate it in "initialize" event of the report.
That's it, it won't be easy but these elements should help to achieve this report.

Related

Getting data out of JSON array list based on a chosen number

I want to process an arraylist that comes from another object but I want it to only make a new JSON array containing student number (which should be the same number as lStudentZelf) & aanwezigheid.
{
"klas": "V1A",
"docent": "Buddha",
"les": "AUI",
"studenten": [{
"studentnummer": 12312591,
"status": "afwezig"
},
{
"studentnummer": 1214531,
"status": "aanwezig"
},
{
"studentnummer": 1214531,
"status": "aanwezig"
}
]
}
(this is how the arraylist looks like, I want it to output only the results of studentnumber 1214531 and keep track of how many times this studentnumber has had the status aanwezig.)
I've tried the code below and aswell tried parsing it on several other ways without avail.
private void ophalen(Conversation conversation) {
JsonObject lJsonObjectIn = (JsonObject) conversation.getRequestBodyAsJSON();
String lGebruikersnaam = lJsonObjectIn.getString("username");
Student lStudentZelf = informatieSysteem.getStudent(lGebruikersnaam);
int lNummerZelf = lStudentZelf.getStudentNummer(); //lNummerZelf will be the number that needs to be the same as the 'studentennummer' in the list.
ArrayList<Statistiek> Test2 = Statistiek.getStatistieken();
JsonArrayBuilder lJsonArrayBuilder = Json.createArrayBuilder();
for (Statistiek s : Test2) {
JsonObjectBuilder lJsonObjectBuilder= Json.createObjectBuilder();
lJsonObjectBuilder.add("aanwezig", aanwezig);
lJsonObjectBuilder.add("afwezig", afwezig);
lJsonObjectBuilder.add("geoorloofd", gafwezig);
lJsonObjectBuilder.add("vak", vak);
lJsonArrayBuilder.add(lJsonObjectBuilder);
}
String lJsonOutStr = lJsonArrayBuilder.build().toString();
conversation.sendJSONMessage(lJsonOutStr);
}
}
In the end I want that it only reads the studentnummer and status part, and keeps track of how many times a specific status appeared (like aanwezig 2x for studentnummer 1214531) belonging to a chosen studentnummer.

How do I write mongo aggregation reduce query in Spring?

data in mongo :
enter image description here
db.test2.aggregate([
{
"$project" : {
"contents" : 1,
"comments" : {
"$filter" : {
"input" : "$comments",
"as" : "item",
"cond" : {"$gt" : ['$$item.score', 2]}
},
},
"comments2" : {
"$filter" : {
"input" : "$comments2",
"as" : "item",
"cond" : {"$gt" : ["$$item.score", 5]}
}
}
}
},
{
"$project" : {
"content" : 1,
"commentsTotal" : {
"$reduce" : {
"input" : "$comments",
"initialValue" : 0,
"in" : {"$add" : ["$$value", "$$this.score"]}
}
},
"comments2Total" : {
"$reduce" : {
"input" : "$comments2",
"initialValue" : 0,
"in" : {"$add" : ["$$value", "$$this.score"]}
}
}
}
},
{$skip : 0},
{$limit: 3}
]);
<!-- language: lang-json-->
So you can see, this does the following :
1、filter the comments and comments2 which score is gt 5.
2、count total of the socre in comment array.
and i write the aggregation query in Spring like this:
AggregationExpression reduce = ArithmeticOperators.Add.valueOf("$$value").add("$$this.socre");
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.project().andExclude("_id")
.andInclude("content")
.and("comments").filter("item", ComparisonOperators.Gt.valueOf("item.score").greaterThanValue(3)).as("comments")
.and("comments2").filter("item", ComparisonOperators.Gt.valueOf("item.score").greaterThanValue(3)).as("comments2"),
Aggregation.project("comments", "comments2")
.and(ArrayOperators.Reduce.arrayOf("comments").withInitialValue("0").reduce(reduce)).as("commentsTotal")
);
when i run like up , it will throws exception :
java.lang.IllegalArgumentException: Invalid reference '$$value'!
You can try below aggregation by wrapping $filter inside the $reduce operation.
Something like below
AggregationExpression reduce1 = new AggregationExpression() {
#Override
public DBObject toDbObject(AggregationOperationContext aggregationOperationContext) {
DBObject filter = new BasicDBObject("$filter", new BasicDBObject("input", "$comments").append("as", "item").append("cond",
new BasicDBObject("$gt", Arrays.<Object>asList("$$item.score", 2))));
DBObject reduce = new BasicDBObject("input", filter).append("initialValue", 0).append("in", new BasicDBObject("$add", Arrays.asList("$$value", "$$this.socre")));
return new BasicDBObject("$reduce", reduce);
}
};
Aggregation aggregation = newAggregation(
Aggregation.project().andExclude("_id")
.andInclude("content")
.and(reduce1).as("commentsTotal")
);
This is an old question, but in case some one winds up here like me, here's how I was able to solve it.
You cannot access "$$this" and "$$value" variables directly like this in spring.
AggregationExpression reduce = ArithmeticOperators.Add.valueOf("$$value").add("$$this.socre");
To do this we have to use reduce variable enum, like this:
AggregationExpression reduce = ArithmeticOperators.Add.valueOf(ArrayOperators.Reduce.Variable.VALUE.getTarget()).add(ArrayOperators.Reduce.Variable.THIS.referringTo("score").getTarget());
Hope this helps!
I had to solve next task and hadn't find any solutions. So i hope my answer will help somebody.
User with roles (user have list of rights + list of roles, each role have own list of rights, needed to find full list of rights):
user structure
role structure
First, i lookup roles to roleDto (for example), then i collect rights from roles to 1 list:
ArrayOperators.Reduce reduce = ArrayOperators.Reduce.arrayOf("$roleDto.rights")
.withInitialValue(new ArrayList<>())
.reduce(ArrayOperators.ConcatArrays.arrayOf("$$value").concat("$$this"));
As result in reduce i have this 1 list of rights collected from roles.
After that i make:
SetOperators.SetUnion.arrayAsSet(reduce).union("$rights")
using previous result. Result type is AggregationExpression because AbstractAggregationExpression implements AggregationExpression.
So, finally i get smth like this (sorry for messy code):
private static AggregationExpression getAllRightsForUser() {
// concat rights from list of roles (each role have list of rights) - list of list to list
ArrayOperators.Reduce reduce = ArrayOperators.Reduce.arrayOf("$roleDto.rights")
.withInitialValue(new ArrayList<>())
.reduce(ArrayOperators.ConcatArrays.arrayOf("$$value").concat("$$this"));
// union result with user.rights
return SetOperators.SetUnion.arrayAsSet(reduce).union("$rights");
}
Result of this operation can be finally used somewhere like here ;) :
public static AggregationOperation addFieldOperation(AggregationExpression aggregationExpression, String fieldName) {
return aoc -> new Document("$addFields", new Document(fieldName, aggregationExpression.toDocument(aoc)));
}
I had the same issue, one of the solutions is to create a custom Reduce function, here's Union example:
public class SetUnionReduceExpression implements AggregationExpression {
#Override
public Document toDocument(AggregationOperationContext context) {
return new Document("$setUnion", ImmutableList.of("$$value", "$$this"));
}
}

Json parsing: Iterating through the values?

I'm trying to use json.simple to get things from this json file:
"Main": {
"Part1":{
"Length": 2,
"Flags": 2,
"Sequence": 4
},
"Part2":{
"Length": 2,
"Type":2,
"Main_Dest":4,
"Main_Source":4,
"Sequence":4,
"Data": {
"1":12,
"2":24
},
"Blank": 8
}
}
Basically, I want to reach the "Type" value in Part2, and on the way add all values. Meaning in the end I want to have the sum 10 (length+flags+sequence+length) and the number 2 for the value "Type". My main problem here is that I have to do it generically, so I can't just collect the values by name because they might change or additional values could be added. Only the value "Type" will always be called exactly that.
What I've done so far is this:
private static void parseJson() {
String path = "...config.json";
boolean count = false;
int sum = 0;
try {
FileReader reader = new FileReader(path);
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(reader);
jsonObject.entrySet();
JSONObject main = (JSONObject) jsonObject.get("Main");
for (Iterator iterator = main.keySet().iterator(); iterator.hasNext();){
String key = (String) iterator.next();
//this is where I'm stumped. Do I keep going into the JSONObject until I get to a value?
if (count){
sum += (int) sahara.get(key);
}
if (key.equals("Type")){
count = true;
}
}
System.out.println(skip);
} catch (Exception e) {
}
}
Obviously I don't really know what I'm doing with this. How do I iterate the lowest level in the json file?
As a little side question, which Json parser libraries should I use if I might sell my software? In other words, which doesn't cause licensing issues?
You can iterate over keys recursively, but you can't calculate sum, result will be unpredictable.
jsonObject.keySet not guarantee returns the keys in the same order as they appears in file.
Use the stream API for Json.
I have added the missing curly braces to fix your input.
{
"Main": {
"Part1":{
"Length": 2,
"Flags": 2,
"Sequence": 4
},
"Part2":{
"Length": 2,
"Type":2,
"Main_Dest":4,
"Main_Source":4,
"Sequence":4,
"Data": {
"1":12,
"2":24
},
"Blank": 8
}
}
}
This examples shows how to use the stream API.
// -*- compile-command: "javac -cp javax.json-1.0.jar q43737601.java && java -cp .:javax.json-1.0.jar q43737601"; -*-
import java.io.FileReader;
import javax.json.Json;
import javax.json.stream.JsonParser;
class q43737601
{
public static void main (String argv[]) throws Exception
{
String path = "config.json";
int sum = 0;
JsonParser p = Json.createParser (new FileReader (path));
while (p.hasNext()) {
JsonParser.Event e = p.next();
switch (e) {
case VALUE_NUMBER:
sum += Integer.parseInt(p.getString());
break;
case KEY_NAME:
if ("Type".equals(p.getString()))
System.out.println(sum);
break;
}
}
}
}
If you run it, it displays 10. The example sums up all numbers up to a key called "Type".
I tried the above example with OpenJDK. It was necessary to follow the steps explained in this answer. I had to set the class path (-cp) in the compile command.

Having a String or int with a binding to a Property

I need to make a programm in JavaFX where I need to manage a movie library. I have a tableview that is filled by an observable list. Then I made bindings between the Properties and the Textboxes or Labels next to the tableview. Now the problem is, I have also pictures that are related to the movies, like cinema posters. and at the moment I just use a hardcoded one:
imgPoster = new Image(getClass().getResourceAsStream("../resources/images/posters/" + 5 + ".jpg"));
In the datafile there is one column with the ID of the movie and the same number is also the picture for it. So I need to replace that "5" in the sample code with a binding or so that it actively changes the picture as soon as I click on a row in the tableview.
How do I do that?
edit:
After the first comment i did that:
imgOscars = new Image(getClass().getResourceAsStream("../resources/images/oscar/Oscar-logo.png"));
oscars = new ImageView(imgOscars);
oscars.setPreserveRatio(true);
oscars.setFitHeight(45);
but question stays
Either add a listener to the selected item property in the table (I am just guessing at the model you are using for the table):
TableView<Movie> table = ... ;
table.getSelectionModel().selectedItemProperty().addListener((obs, oldSelectedMovie, newSelectedMovie) -> {
if (newSelectedMovie == null) {
oscars.setImage(null);
} else {
// get image from new selection
Image image = new Image(getClass().getResourceAsStream("../resources/images/oscar/"+newSelectedMoviegetId()+".png"));
oscars.setImage(image);
}
});
or use a binding:
oscars.imageProperty().bind(Bindings.createObjectBinding(() -> {
if (table.getSelectionModel().getSelectedItem() == null) {
return null ;
} else {
Movie selected = table.getSelectionModel().getSelectedItem();
return new Image(getClass().getResourceAsStream("../resources/images/oscar/"+selected.getId()+".png")); ;
},
table.getSelectionModel().selectedItemProperty());

Getting the metadata of columns from SOQL Query

I have a simple SOQL query in java for extracting Salesforce standard object as follows -
String soqlQuery = "SELECT FirstName, LastName FROM Contact";
QueryResult qr = connection.query(soqlQuery);
I want to get the datatype of the object fields.
I have written a small function below which will provide the list of Phone fields and its label present in a Custom or Standard Object of your Salesforce ORG. I hope this might help you in writing the business logic for your code.
public list<String> getFieldsForSelectedObject(){
selectedPhoneNumber = ''; //to reset home number field
list<String> fieldsName = new list<String>();
selectedObject = 'Object Name' // This should have the object name for which we want to get the fields type
schemaMap = Schema.getGlobalDescribe(); //Populating the schema map
try{
if(selectedObject != null || selectedObject != '' || selectedObject != '--Select Object--'){
Map<String, Schema.SObjectField> fieldMap = schemaMap.get(selectedObject).getDescribe().fields.getMap();
for(Schema.SObjectField sfield : fieldMap.Values()){
schema.describefieldresult dfield = sfield.getDescribe();
schema.Displaytype disfield= dfield.getType();
system.debug('#######' + dfield );
if(dfield.getType() == Schema.displayType.Phone){// Over here I am trying to findout all the PHONE Type fields in the object(Both Custom/Standard)
fieldsName.add('Name:'+dfield.getName() +' Label:'+ dfield.getLabel ());
}
}
}
}catch(Exception ex){
apexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'There is no Phone or Fax Field Exist for selected Object!'));
}
return fieldsName;
}
Sample OUTPUT List of String::
Name: Home_Phone__c Label: Home Phone
Name: Office_Phone__c Label: Office Phone
Say that we have the below soql.
select FirstName,LastName from Contact limit 2
The query result in the QueryResult object looks like below.
{
[2]XmlObject
{
name={urn:partner.soap.sforce.com}records, value=null, children=
[
XmlObject{name={urn:sobject.partner.soap.sforce.com}type, value=Contact, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}Id, value=null, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}FirstName, value=Bill, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}LastName, value=Gates, children=[]}
]
},
XmlObject
{
name={urn:partner.soap.sforce.com}records, value=null, children=
[
XmlObject{name={urn:sobject.partner.soap.sforce.com}type, value=Contact, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}Id, value=null, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}FirstName, value=Alan, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}LastName, value=Donald, children=[]}
]
},
}
In order to parse the QueryResult and to take column names, I have implemented the below method that will return the column names in comma separated String. I have mentioned the logic inside the code.
public String getColumnNames(QueryResult soqlResponse)
{
String columns = ""
try
{
// We are looping inorder to pick the 1st record from the QueryResult
for (SObject record : soqlResponse.getRecords())
{
Iterator<XmlObject> xmlList = record.getChildren();
int counterXml = 0;
while(xmlList.hasNext())
{
XmlObject xObj = xmlList.next();
// Since the 1st 2 nodes contains metadata of some other information, we are starting from the 3rd node only
if(counterXml > 1)
{
columns += xObj.getName().getLocalPart() + ",";
}
counterXml++;
}
// Since we can get the column names from the 1st record, we are breaking the loop after the data of 1st record is read
break;
}
// We are removing the last comma in the string
columns = columns.substring(0, columns.length() - 1);
}
catch(Exception ex)
{
}
return columns;
}

Categories

Resources