2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.transform.jsonpath.internal;
15 import java.util.List;
16 import java.util.stream.Collectors;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.openhab.core.transform.TransformationException;
21 import org.openhab.core.transform.TransformationService;
22 import org.openhab.core.types.UnDefType;
23 import org.osgi.service.component.annotations.Component;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
27 import com.jayway.jsonpath.InvalidJsonException;
28 import com.jayway.jsonpath.InvalidPathException;
29 import com.jayway.jsonpath.JsonPath;
30 import com.jayway.jsonpath.PathNotFoundException;
34 * The implementation of {@link TransformationService} which transforms the input by JSonPath Expressions.
36 * @author Gaƫl L'hopital
37 * @author Sebastian Janzen
41 @Component(property = { "openhab.transform=JSONPATH" })
42 public class JSonPathTransformationService implements TransformationService {
44 private final Logger logger = LoggerFactory.getLogger(JSonPathTransformationService.class);
47 * Transforms the input <code>source</code> by JSonPath expression.
49 * @param jsonPathExpression JsonPath expression
50 * @param source String which contains JSON
51 * @throws TransformationException If the JsonPath expression is invalid, an {@link InvalidPathException} is thrown,
52 * which is encapsulated in a {@link TransformationException}.
55 public @Nullable String transform(String jsonPathExpression, String source) throws TransformationException {
56 if (jsonPathExpression == null || source == null) {
57 throw new TransformationException("the given parameters 'JSonPath' and 'source' must not be null");
60 logger.debug("about to transform '{}' by the function '{}'", source, jsonPathExpression);
62 if (source.isBlank()) {
63 // return null if source is empty/blank, JSONPath will throw an IAE on empty input strings
67 Object transformationResult = JsonPath.read(source, jsonPathExpression);
68 logger.debug("transformation resulted in '{}'", transformationResult);
69 if (transformationResult == null) {
71 } else if (transformationResult instanceof List list) {
72 return flattenList(list);
74 return transformationResult.toString();
76 } catch (PathNotFoundException e) {
77 throw new TransformationException("Invalid path '" + jsonPathExpression + "' in '" + source + "'");
78 } catch (InvalidPathException | InvalidJsonException e) {
79 throw new TransformationException("An error occurred while transforming JSON expression.", e);
83 private String flattenList(List<?> list) {
84 if (list.size() == 1) {
85 return list.get(0).toString();
87 if (list.size() > 1) {
88 if (list.get(0) instanceof Number || list.get(0) instanceof Boolean) {
89 return createNumberList(list);
90 } else if (list.get(0) instanceof String) {
91 return createStringList(list);
94 "JsonPath expressions with more than one result are only supported for Boolean, Number and String data types, please adapt your selector. Result: {}",
97 return UnDefType.NULL.toFullString();
100 private String createNumberList(List<?> list) {
101 return list.stream().map(n -> String.valueOf(n)).collect(Collectors.joining(", ", "[", "]"));
104 private String createStringList(List<?> list) {
105 return list.stream().map(n -> "\"" + String.valueOf(n) + "\"").collect(Collectors.joining(", ", "[", "]"));