Az esetek többségében a Jasper Studio használata a vizuális felületen való kattintgatásban kimerül, de speciális esetekben szükség lehet a forráskód mélyebb ismeretére is. Ilyen eset lehet a listák kezelése kapcsán használandó JsonDataSource osztály kódja vagy a hibakezelés is.
A forráskódot a https://sourceforge.net/projects/jasperreports/files/jasperreports oldalról tölthetjük le, ha kiválasztjuk a szükséges verziót (pl: 6.2.2), majd a "jasperreports-x.x.x-project.zip" elemet választva. A letöltött fájl kicsomagolása után importálhatjuk az Eclipse-be Maven projektként.
A JsonDataSource kódjának részlete:
public class JsonDataSource extends JRAbstractTextDataSource implements JsonData {
...
@Override
public Object getFieldValue(JRField jrField) throws JRException {
if (currentJsonNode == null) {
return null;
}
String expression = jrField.getDescription();
if (expression == null || expression.length() == 0) {
expression = jrField.getName();
if (expression == null || expression.length() == 0) {
return null;
}
}
Object value = null;
Class<?> valueClass = jrField.getValueClass();
JsonNode selectedObject = getJsonData(currentJsonNode, expression);
if (Object.class != valueClass) {
boolean hasValue = selectedObject != null && !selectedObject.isMissingNode() && !selectedObject.isNull();
if (hasValue) {
try {
if (valueClass.equals(String.class)) {
if (selectedObject.isArray()) {
value = selectedObject.toString();
} else {
value = selectedObject.asText();
}
} else if (valueClass.equals(Boolean.class)) {
value = selectedObject.booleanValue();
} else if (Number.class.isAssignableFrom(valueClass)) {
value = convertStringValue(selectedObject.asText(), valueClass);
} else if (Date.class.isAssignableFrom(valueClass)) {
value = convertStringValue(selectedObject.asText(), valueClass);
} else {
throw new JRException(EXCEPTION_MESSAGE_KEY_CANNOT_CONVERT_FIELD_TYPE, new Object[] { jrField.getName(),
valueClass.getName() });
}
} catch (Exception e) {
throw new JRException(EXCEPTION_MESSAGE_KEY_JSON_FIELD_VALUE_NOT_RETRIEVED, new Object[] { jrField.getName(),
valueClass.getName() }, e);
}
}
} else {
value = selectedObject;
}
return value;
}
...
}
List helyett ArrayNode
A fenti kódban látszik, hogy a mező visszatérési értéke csak String, Number, Boolean vagy Date lehet. Ha egy lista elemeit szeretnénk visszakapni java.util.List típusként, akkor csalódnunk kell. Ebben az esetben csak java.lang.Object-ként kérhetjük el, de még így sem cast-olhatjuk List-re, hanem csak JsonNode lehet (selectedObject). Látható az is, hogy ha tömböt String-ként kérünk el, akkor azt ugyan visszakapjuk, de már csak String-ként dolgozhatunk vele (["", "", ""]). A tömb elemeinek számát például nem kérdezhetjük le így (csak csűrés-csavarással: replace + Arrays.asList).
Hogyan tudjuk meg egy adott lista elemeinek számát?
A megoldást a kódban továbbugrálva kaphatjuk meg: getJsonData - goDownPathWithAttribute - goDownPath:
protected JsonNode goDownPath(JsonNode rootNode, String simplePath) {
if(rootNode != null && !rootNode.isMissingNode()) {
JsonNode result = null;
if (rootNode.isObject()) {
result = rootNode.path(simplePath);
} else if (rootNode.isArray()) {
result = mapper.createArrayNode();
for (JsonNode node: rootNode) {
JsonNode deeperNode = node.path(simplePath);
if (!deeperNode.isMissingNode()) {
if (deeperNode.isArray()) {
for(JsonNode arrayNode: deeperNode) {
((ArrayNode)result).add(arrayNode);
}
} else {
((ArrayNode)result).add(deeperNode);
}
}
}
}
return result;
}
return rootNode;
}
A createArrayNode() visszatérési értéke com.fasterxml.jackson.databind.node.ArrayNode, amelynek már van size() metódusa, így megkaphatjuk az adott lista elemeinek számát is.
Visszatérési érték bővítése List-tel
A fenti getFieldValue() metódust bővíthetjük a következő kódrészlettel:
@Override
public Object getFieldValue(JRField jrField) throws JRException {
...
if (Object.class != valueClass) {
boolean hasValue = selectedObject != null && !selectedObject.isMissingNode() && !selectedObject.isNull();
if (hasValue) {
try {
if (valueClass.equals(String.class)) {
...
} else if (List.class.equals(valueClass)) {
List<Object> result = new ArrayList<Object>();
if (!selectedObject.isMissingNode() && selectedObject.isArray()) {
value = this.convertToList(selectedObject, result);
} else {
value = result;
}
} ...
} catch (Exception e) {
...
}
}
} ...
return value;
}
A listát összepakoló metódus szöveges elemként teszi be a listaelemeket, függetlenül azok típusától (a jelenlegi cél a lista elemszámának kiderítése). A listába ágyazott mélységi listák felderítését a rekurzivitás biztosítja.
private List<Object> convertToList(JsonNode selectedObject, List<Object> result) {
for (JsonNode node : selectedObject) {
if (!node.isMissingNode() && node.isArray()) {
List<Object> innerResult = new ArrayList<Object>();
result.add(innerResult);
this.convertToList(node, innerResult);
} else {
// don't care about type
result.add(node.asText());
}
}
return result;
}
A módosított kód használata
A maven install után a target mappában találjuk a "jasperreports-6.2.2.jar" fájlt. A Jasper Studio telepített mappájában keressünk rá a "jasperreports*.jar"-ra és a 6.2.2-es verziót tartalmazzó mappában írjuk felül a legenerált jar fájllal az ottani jart (a mappa helye a frissítésektől függ). Ez a fájl nálam a következő helyen található:
..\configuration\org.eclipse.osgi\506\0\.cp\lib\jasperreports-6.2.2-20160505.150232-15.jar
Az $F{issues} mező class property-jét Object-ről java.util.List-re állítjuk. Így, ha egy jármű bejegyzéseinek számára vagyunk kíváncsiak, akkor a korábban használt kifejezés helyett az $V{issueSize} változóban egy egyszerűbbet használhatunk (nem kell a MissingNode miatti feltétel és nem kell cast-olni sem):
//($F{issues} instanceof ArrayNode) ? ((ArrayNode)$F{issues}).size() : 0;
$F{issues}.size()

























