1
24 package net.sf.jasperreports.engine.data;
25
26 import java.io.File;
27 import java.io.FileNotFoundException;
28 import java.io.InputStream;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.StringTokenizer;
36
37 import com.fasterxml.jackson.databind.JsonNode;
38 import com.fasterxml.jackson.databind.ObjectMapper;
39 import com.fasterxml.jackson.databind.node.ArrayNode;
40
41 import net.sf.jasperreports.annotations.properties.Property;
42 import net.sf.jasperreports.annotations.properties.PropertyScope;
43 import net.sf.jasperreports.engine.DefaultJasperReportsContext;
44 import net.sf.jasperreports.engine.JRException;
45 import net.sf.jasperreports.engine.JRField;
46 import net.sf.jasperreports.engine.JRPropertiesUtil;
47 import net.sf.jasperreports.engine.JasperReportsContext;
48 import net.sf.jasperreports.engine.query.JsonQueryExecuterFactory;
49 import net.sf.jasperreports.engine.util.JsonUtil;
50 import net.sf.jasperreports.properties.PropertyConstants;
51 import net.sf.jasperreports.repo.RepositoryContext;
52 import net.sf.jasperreports.repo.SimpleRepositoryContext;
53
54
55
60 public class JsonDataSource extends JRAbstractTextDataSource implements JsonData<JsonDataSource>, RandomAccessDataSource {
61
62 public static final String EXCEPTION_MESSAGE_KEY_JSON_FIELD_VALUE_NOT_RETRIEVED = "data.json.field.value.not.retrieved";
63 public static final String EXCEPTION_MESSAGE_KEY_INVALID_ATTRIBUTE_SELECTION = "data.json.invalid.attribute.selection";
64 public static final String EXCEPTION_MESSAGE_KEY_INVALID_EXPRESSION = "data.json.invalid.expression";
65 public static final String EXCEPTION_MESSAGE_KEY_NO_DATA = "data.json.no.data";
66
67
70 @Property (
71 category = PropertyConstants.CATEGORY_DATA_SOURCE,
72 scopes = {PropertyScope.FIELD},
73 scopeQualifications = {JsonQueryExecuterFactory.JSON_QUERY_EXECUTER_NAME},
74 sinceVersion = PropertyConstants.VERSION_6_3_1
75 )
76 public static final String PROPERTY_FIELD_EXPRESSION = JRPropertiesUtil.PROPERTY_PREFIX + "json.field.expression";
77
78
79 private String selectExpression;
80
81 private Map<String, String> fieldExpressions = new HashMap<String, String>();
82
83 private JsonNode dataNode;
84 private Iterator<JsonNode> jsonNodesIterator;
85
86 private int currentNodeIndex;
87
88
89 private JsonNode currentJsonNode;
90
91 private final String PROPERTY_SEPARATOR = ".";
92
93 private final String ARRAY_LEFT = "[";
94
95 private final String ARRAY_RIGHT = "]";
96
97 private final String ATTRIBUTE_LEFT = "(";
98
99 private final String ATTRIBUTE_RIGHT = ")";
100
101
102 private JsonNode jsonTree;
103
104 private ObjectMapper mapper;
105
106 public JsonDataSource(InputStream stream) throws JRException {
107 this(stream, null);
108 }
109
110 public JsonDataSource(InputStream jsonStream, String selectExpression) throws JRException {
111 this(JsonUtil.parseJson(jsonStream), selectExpression);
112 }
113
114 protected JsonDataSource(JsonNode jsonTree, String selectExpression) throws JRException {
115 this.mapper = JsonUtil.createObjectMapper();
116
117 this.jsonTree = jsonTree;
118 this.selectExpression = selectExpression;
119
120 moveFirst();
121 }
122
123
124 public JsonDataSource(File file) throws FileNotFoundException, JRException {
125 this(file, null);
126 }
127
128
129 public JsonDataSource(File file, String selectExpression) throws FileNotFoundException, JRException {
130 this(JsonUtil.parseJson(file), selectExpression);
131 }
132
133
139 public JsonDataSource(JasperReportsContext jasperReportsContext, String location, String selectExpression) throws JRException
140 {
141 this(SimpleRepositoryContext.of(jasperReportsContext), location, selectExpression);
142 }
143
144 public JsonDataSource(RepositoryContext repositoryContext, String location, String selectExpression) throws JRException
145 {
146 this(JsonUtil.parseJson(repositoryContext, location), selectExpression);
147 }
148
149
152 public JsonDataSource(String location, String selectExpression) throws JRException
153 {
154 this(DefaultJasperReportsContext.getInstance(), location, selectExpression);
155 }
156
157
162 @Override
163 public void moveFirst() throws JRException {
164 if (jsonTree == null || jsonTree.isMissingNode()) {
165 throw
166 new JRException(
167 EXCEPTION_MESSAGE_KEY_NO_DATA,
168 (Object[])null);
169 }
170
171 currentNodeIndex = -1;
172 currentJsonNode = null;
173 JsonNode result = getJsonData(jsonTree, selectExpression);
174 if (result != null && result.isObject()) {
175 dataNode = result;
176 final List<JsonNode> list = new ArrayList<JsonNode>();
177 list.add(result);
178 jsonNodesIterator = new Iterator<JsonNode>() {
179 private int count = -1;
180 @Override
181 public void remove() {
182 list.remove(count);
183 }
184
185 @Override
186 public JsonNode next() {
187 count ++;
188 return list.get(count);
189 }
190
191 @Override
192 public boolean hasNext() {
193 return count < list.size()-1;
194 }
195 };
196 } else if (result != null && result.isArray()) {
197 dataNode = result;
198 jsonNodesIterator = result.elements();
199 }
200 }
201
202
207 @Override
208 public boolean next() {
209 if(jsonNodesIterator == null || !jsonNodesIterator.hasNext()) {
210 return false;
211 }
212 ++currentNodeIndex;
213 currentJsonNode = jsonNodesIterator.next();
214 return true;
215 }
216
217 @Override
218 public int recordCount() {
219 int count;
220 if (dataNode != null) {
221 if (dataNode.isObject()) {
222 count = 1;
223 } else if (dataNode.isArray()) {
224 count = dataNode.size();
225 } else {
226
227 throw new IllegalStateException();
228 }
229 } else {
230 count = 0;
231 }
232 return count;
233 }
234
235 @Override
236 public int currentIndex() {
237 return currentNodeIndex;
238 }
239
240 @Override
241 public void moveToRecord(int index) throws NoRecordAtIndexException {
242 if (dataNode != null) {
243 if (dataNode.isObject()) {
244 if (index == 0) {
245 currentNodeIndex = 0;
246 currentJsonNode = dataNode;
247 } else {
248 throw new NoRecordAtIndexException(index);
249 }
250 } else if (dataNode.isArray()) {
251 if (index >= 0 && index < dataNode.size()) {
252 currentNodeIndex = index;
253 currentJsonNode = dataNode.get(index);
254 } else {
255 throw new NoRecordAtIndexException(index);
256 }
257 }
258 } else {
259 throw new NoRecordAtIndexException(index);
260 }
261 }
262
263
268 @Override
269 public Object getFieldValue(JRField jrField) throws JRException
270 {
271 if(currentJsonNode == null) {
272 return null;
273 }
274
275 String expression = null;
276 if (fieldExpressions.containsKey(jrField.getName()))
277 {
278 expression = fieldExpressions.get(jrField.getName());
279 }
280 else
281 {
282 expression = getFieldExpression(jrField);
283 fieldExpressions.put(jrField.getName(), expression);
284 }
285 if (expression == null || expression.length() == 0)
286 {
287 return null;
288 }
289
290 Object value = null;
291
292 Class<?> valueClass = jrField.getValueClass();
293 JsonNode selectedObject = getJsonData(currentJsonNode, expression);
294
295 if(Object.class != valueClass)
296 {
297 boolean hasValue = selectedObject != null
298 && !selectedObject.isMissingNode() && !selectedObject.isNull();
299 if (hasValue)
300 {
301 try {
302 if (valueClass.equals(String.class)) {
303 if (selectedObject.isArray()) {
304 value = selectedObject.toString();
305 } else {
306 value = selectedObject.asText();
307 }
308
309 } else if (valueClass.equals(Boolean.class)) {
310 value = selectedObject.booleanValue();
311
312 } else if (Number.class.isAssignableFrom(valueClass)) {
313
314 value = convertStringValue(selectedObject.asText(), valueClass);
315
316 }
317 else if (Date.class.isAssignableFrom(valueClass)) {
318 value = convertStringValue(selectedObject.asText(), valueClass);
319
320 } else {
321 throw
322 new JRException(
323 EXCEPTION_MESSAGE_KEY_CANNOT_CONVERT_FIELD_TYPE,
324 new Object[]{jrField.getName(), valueClass.getName()});
325 }
326 } catch (Exception e) {
327 throw
328 new JRException(
329 EXCEPTION_MESSAGE_KEY_JSON_FIELD_VALUE_NOT_RETRIEVED,
330 new Object[]{jrField.getName(), valueClass.getName()},
331 e);
332 }
333 }
334 }
335 else
336 {
337 value = selectedObject;
338 }
339
340 return value;
341 }
342
343
350 protected JsonNode getJsonData(JsonNode rootNode, String jsonExpression) throws JRException {
351 if (jsonExpression == null || jsonExpression.length() == 0) {
352 return rootNode;
353 }
354 JsonNode tempNode = rootNode;
355 StringTokenizer tokenizer = new StringTokenizer(jsonExpression, PROPERTY_SEPARATOR);
356
357 while(tokenizer.hasMoreTokens()) {
358 String currentToken = tokenizer.nextToken();
359 int currentTokenLength = currentToken.length();
360 int indexOfLeftSquareBracket = currentToken.indexOf(ARRAY_LEFT);
361
362
363 if (indexOfLeftSquareBracket != -1) {
364
365 if(currentToken.lastIndexOf(ARRAY_RIGHT) != (currentTokenLength-1)) {
366 throw
367 new JRException(
368 EXCEPTION_MESSAGE_KEY_INVALID_EXPRESSION,
369 new Object[]{jsonExpression, currentToken});
370 }
371
372
373 if (indexOfLeftSquareBracket > 0) {
374
375 String property = currentToken.substring(0, indexOfLeftSquareBracket);
376 tempNode = goDownPathWithAttribute(tempNode, property);
377 }
378
379 String arrayOperators = currentToken.substring(indexOfLeftSquareBracket);
380 StringTokenizer arrayOpsTokenizer = new StringTokenizer(arrayOperators,ARRAY_RIGHT);
381 while(arrayOpsTokenizer.hasMoreTokens()) {
382 if (tempNode == null || tempNode.isMissingNode() || !tempNode.isArray()) {
383 return null;
384 }
385
386 String currentArrayOperator = arrayOpsTokenizer.nextToken();
387 tempNode = tempNode.path(Integer.parseInt(currentArrayOperator.substring(1)));
388 }
389 } else {
390 tempNode = goDownPathWithAttribute(tempNode, currentToken);
391 }
392 }
393
394 return tempNode;
395 }
396
397
398
405 protected JsonNode goDownPathWithAttribute(JsonNode rootNode, String pathWithAttributeExpression) throws JRException {
406
407 int indexOfLeftRoundBracket = pathWithAttributeExpression.indexOf(ATTRIBUTE_LEFT);
408 if (indexOfLeftRoundBracket != -1) {
409
410
411 if(pathWithAttributeExpression.indexOf(ATTRIBUTE_RIGHT) != (pathWithAttributeExpression.length() - 1)) {
412 throw
413 new JRException(
414 EXCEPTION_MESSAGE_KEY_INVALID_ATTRIBUTE_SELECTION,
415 new Object[]{pathWithAttributeExpression});
416 }
417
418 if(rootNode != null && !rootNode.isMissingNode()) {
419
420 String path = pathWithAttributeExpression.substring(0, indexOfLeftRoundBracket);
421
422
423 String attributeExpression = pathWithAttributeExpression.substring(indexOfLeftRoundBracket + 1, pathWithAttributeExpression.length() - 1);
424
425 JsonNode result = null;
426 if (rootNode.isObject()) {
427
428 if (!rootNode.path(path).isMissingNode()) {
429 if (rootNode.path(path).isObject()) {
430 if (isValidExpression(rootNode.path(path), attributeExpression)) {
431 result = rootNode.path(path);
432 }
433 } else if (rootNode.path(path).isArray()) {
434 result = mapper.createArrayNode();
435 for (JsonNode node: rootNode.path(path)) {
436 if (isValidExpression(node, attributeExpression)) {
437 ((ArrayNode)result).add(node);
438 }
439 }
440 }
441 }
442 } else if (rootNode.isArray()) {
443 result = mapper.createArrayNode();
444 for (JsonNode node: rootNode) {
445 JsonNode deeperNode = node.path(path);
446 if (!deeperNode.isMissingNode()) {
447 if (deeperNode.isArray()) {
448 for(JsonNode arrayNode: deeperNode) {
449 if (isValidExpression(arrayNode, attributeExpression)) {
450 ((ArrayNode)result).add(arrayNode);
451 }
452 }
453 } else if (isValidExpression(deeperNode, attributeExpression)){
454 ((ArrayNode)result).add(deeperNode);
455 }
456 }
457 }
458 }
459 return result;
460 }
461
462 } else {
463 return goDownPath(rootNode, pathWithAttributeExpression);
464 }
465 return rootNode;
466 }
467
468
469
475 protected JsonNode goDownPath(JsonNode rootNode, String simplePath) {
476 if(rootNode != null && !rootNode.isMissingNode()) {
477 JsonNode result = null;
478 if (rootNode.isObject()) {
479 result = rootNode.path(simplePath);
480 } else if (rootNode.isArray()) {
481 result = mapper.createArrayNode();
482 for (JsonNode node: rootNode) {
483 JsonNode deeperNode = node.path(simplePath);
484 if (!deeperNode.isMissingNode()) {
485 if (deeperNode.isArray()) {
486 for(JsonNode arrayNode: deeperNode) {
487 ((ArrayNode)result).add(arrayNode);
488 }
489 } else {
490 ((ArrayNode)result).add(deeperNode);
491 }
492 }
493 }
494 }
495 return result;
496 }
497 return rootNode;
498 }
499
500
501
508 protected boolean isValidExpression(JsonNode operand, String attributeExpression) throws JRException {
509 return JsonUtil.evaluateJsonExpression(operand, attributeExpression);
510 }
511
512
513
519 @Override
520 public JsonDataSource subDataSource() throws JRException {
521 return subDataSource(null);
522 }
523
524
525
534 @Override
535 public JsonDataSource subDataSource(String selectExpression) throws JRException {
536 if(currentJsonNode == null)
537 {
538 throw
539 new JRException(
540 EXCEPTION_MESSAGE_KEY_NODE_NOT_AVAILABLE,
541 (Object[])null);
542 }
543
544 JsonDataSource subDataSource = new JsonDataSource(currentJsonNode, selectExpression);
545 subDataSource.setTextAttributes(this);
546
547 return subDataSource;
548 }
549
550
551
554 @Deprecated
555 public void close() {
556
557 }
558
559
560 protected String getFieldExpression(JRField field)
561 {
562 String fieldExpression = null;
563 if (field.hasProperties())
564 {
565 fieldExpression = field.getPropertiesMap().getProperty(PROPERTY_FIELD_EXPRESSION);
566 }
567 if (fieldExpression == null)
568 {
569 fieldExpression = field.getDescription();
570 if (fieldExpression == null || fieldExpression.length() == 0)
571 {
572 fieldExpression = field.getName();
573 }
574 }
575 return fieldExpression;
576 }
577 }
578