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.jinja.internal;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
21 import java.util.Map.Entry;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.core.transform.TransformationException;
26 import org.openhab.core.transform.TransformationService;
27 import org.osgi.service.component.annotations.Component;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
31 import com.fasterxml.jackson.databind.JsonNode;
32 import com.fasterxml.jackson.databind.ObjectMapper;
33 import com.hubspot.jinjava.Jinjava;
34 import com.hubspot.jinjava.JinjavaConfig;
35 import com.hubspot.jinjava.interpret.FatalTemplateErrorsException;
39 * The implementation of {@link TransformationService} which transforms the input by Jinja2 Expressions.
41 * @author Jochen Klein - Initial contribution
45 @Component(property = { "openhab.transform=JINJA" })
46 public class JinjaTransformationService implements TransformationService {
48 private final Logger logger = LoggerFactory.getLogger(JinjaTransformationService.class);
50 private final JinjavaConfig config = JinjavaConfig.newBuilder().withFailOnUnknownTokens(true).build();
51 private final Jinjava jinjava = new Jinjava(config);
54 * Transforms the input <code>value</code> by Jinja template.
56 * @param template Jinja template
57 * @param value String may contain JSON
58 * @throws TransformationException
61 public @Nullable String transform(String template, String value) throws TransformationException {
62 String transformationResult;
63 Map<String, @Nullable Object> bindings = new HashMap<>();
65 logger.debug("about to transform '{}' by the function '{}'", value, template);
67 bindings.put("value", value);
70 JsonNode tree = new ObjectMapper().readTree(value);
71 bindings.put("value_json", toObject(tree));
72 } catch (IOException e) {
73 // ok, then value_json is null...
77 transformationResult = jinjava.render(template, bindings);
78 } catch (FatalTemplateErrorsException e) {
79 throw new TransformationException("An error occurred while transformation. " + e.getMessage(), e);
82 logger.debug("transformation resulted in '{}'", transformationResult);
84 return transformationResult;
87 private static @Nullable Object toObject(JsonNode node) {
88 switch (node.getNodeType()) {
90 List<@Nullable Object> result = new ArrayList<>();
91 for (JsonNode el : node) {
92 result.add(toObject(el));
97 return node.decimalValue();
99 Map<String, @Nullable Object> result = new HashMap<>();
100 Iterator<Entry<String, JsonNode>> it = node.fields();
101 while (it.hasNext()) {
102 Entry<String, JsonNode> field = it.next();
103 result.put(field.getKey(), toObject(field.getValue()));
108 return node.asText();
110 return node.asBoolean();