Open pktest6 opened 3 years ago
It's possible with custom modifies
package br.com.teste.jolt;
import com.bazaarvoice.jolt.common.Optional;
import com.bazaarvoice.jolt.modifier.function.Function;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CustomFunctions {
public static final class recursivelyReplace extends Function.ListFunction {
@Override
protected Optional<Object> applyList(List<Object> argList) {
if (argList.size() != 3) {
return Optional.empty();
}
Object objectValue = argList.get(0);
String from = (String) argList.get(1);
String to = (String) argList.get(2);
recursivelyReplace(objectValue, from, to);
return Optional.of(objectValue);
}
}
public static void recursivelyReplace(Object input, String from, String to) {
if (input instanceof List) {
List inputList = (List) input;
inputList.forEach(i -> recursivelyReplace(i, from, to));
} else if (input instanceof Map) {
Map<String, Object> inputMap = (Map<String, Object>) input;
replace(inputMap, from, to);
for (Map.Entry<String, Object> entry : inputMap.entrySet()) {
recursivelyReplace(entry.getValue(), from, to);
}
}
}
public static void replace(Map<String, Object> inputMap, String from, String to) {
Set<String> keys = new HashSet<>(inputMap.keySet());
for (String key : keys) {
if (key.equals(from)) {
Object removedValue = inputMap.remove(from);
inputMap.put(to, removedValue);
}
}
}
}
package br.com.teste.jolt;
import com.bazaarvoice.jolt.ContextualTransform;
import com.bazaarvoice.jolt.SpecDriven;
import com.bazaarvoice.jolt.common.Optional;
import com.bazaarvoice.jolt.common.tree.MatchedElement;
import com.bazaarvoice.jolt.common.tree.WalkedPath;
import com.bazaarvoice.jolt.exception.SpecException;
import com.bazaarvoice.jolt.modifier.OpMode;
import com.bazaarvoice.jolt.modifier.TemplatrSpecBuilder;
import com.bazaarvoice.jolt.modifier.function.Function;
import com.bazaarvoice.jolt.modifier.spec.ModifierCompositeSpec;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class CustomModifier implements SpecDriven, ContextualTransform {
private static final Map<String, Function> STOCK_FUNCTIONS = new HashMap<>();
private final ModifierCompositeSpec rootSpec;
static {
STOCK_FUNCTIONS.put("recursivelyReplace", new CustomFunctions.recursivelyReplace());
}
@SuppressWarnings("unchecked")
private CustomModifier(Object spec, OpMode opMode, Map<String, Function> functionsMap) {
if (spec == null) {
throw new SpecException(opMode.name() + " expected a spec of Map type, got 'null'.");
}
if (!(spec instanceof Map)) {
throw new SpecException(opMode.name() + " expected a spec of Map type, got " + spec.getClass().getSimpleName());
}
if (functionsMap == null || functionsMap.isEmpty()) {
throw new SpecException(opMode.name() + " expected a populated functions' map type, got " + (functionsMap == null ? "null" : "empty"));
}
functionsMap = Collections.unmodifiableMap(functionsMap);
TemplatrSpecBuilder templatrSpecBuilder = new TemplatrSpecBuilder(opMode, functionsMap);
rootSpec = new ModifierCompositeSpec(ROOT_KEY, (Map<String, Object>) spec, opMode, templatrSpecBuilder);
}
@Override
public Object transform(final Object input, final Map<String, Object> context) {
Map<String, Object> contextWrapper = new HashMap<>();
contextWrapper.put(ROOT_KEY, context);
MatchedElement rootLpe = new MatchedElement(ROOT_KEY);
WalkedPath walkedPath = new WalkedPath();
walkedPath.add(input, rootLpe);
rootSpec.apply(ROOT_KEY, Optional.of(input), walkedPath, null, contextWrapper);
return input;
}
/**
* This variant of modifier creates the key/index is missing,
* and overwrites the value if present
*/
public static final class Overwritr extends CustomModifier {
public Overwritr(Object spec) {
this(spec, STOCK_FUNCTIONS);
}
public Overwritr(Object spec, Map<String, Function> functionsMap) {
super(spec, OpMode.OVERWRITR, functionsMap);
}
}
/**
* This variant of modifier only writes when the key/index is missing
*/
public static final class Definr extends CustomModifier {
public Definr(final Object spec) {
this(spec, STOCK_FUNCTIONS);
}
public Definr(Object spec, Map<String, Function> functionsMap) {
super(spec, OpMode.DEFINER, functionsMap);
}
}
/**
* This variant of modifier only writes when the key/index is missing or the value is null
*/
public static class Defaultr extends CustomModifier {
public Defaultr(final Object spec) {
this(spec, STOCK_FUNCTIONS);
}
public Defaultr(Object spec, Map<String, Function> functionsMap) {
super(spec, OpMode.DEFAULTR, functionsMap);
}
}
}
spec:
[
{
"operation": "shift",
"spec": {
"Parent": {
"primary": {
"value": "PrimaryParent"
},
"children": "children"
}
}
}, {
"operation": "br.com.teste.jolt.CustomModifier$Overwritr",
"spec": {
"children": "=recursivelyReplace(@(1,children), 'type', 'class')"
}
}
]
output
{
"PrimaryParent": true,
"children": [
{
"children": [
{
"children": [
{
"class": "X11"
},
{
"class": "X12"
}
],
"class": "X1"
},
{
"class": "X2"
}
],
"class": "X"
},
{
"children": [
{
"class": "Y1"
},
{
"class": "Y11"
}
],
"class": "Y"
}
]
}
Thank you @lucioalmeida . I was looking for an custom functions example.
Here is my input json { "Parent": { "primary": { "value": true }, "children": [ { "type": "X", "children": [ { "type": "X1", "children": [ { "type": "X11" }, { "type": "X12" } ] }, { "type": "X2" } ] }, { "type": "Y", "children": [ { "type": "Y1" }, { "type": "Y11" } ] } ] } } Children array inside have children array. This is a nested structure that can have n nesting level. I need output json { "PrimaryParent" : true, "children" : [ { "class" : "X", "children" : [ { "class" : "X1", "children" : [ { "class" : "X11" }, { "class" : "X12" } ] }, { "class" : "X2" } ] }, { "class" : "Y", "children" : [ { "class" : "Y1" }, { "class" : "Y11" } ] } ] }
Is there a way to write jolt spec without specifying complete XPath for every 'children' array as it can be nested n levels deep? The spec I have is that looks for hardcoded nested structure three-level deep. [ { "operation": "shift", "spec": { "Parent": { "primary": { // simple match. Put the value '4' in the output under the "Rating" field "value": "PrimaryParent" }, "children": { "": { "type": "children[&1].class", "children": { "": { "type": "children[&3].children[&1].class", "children": { "*": { "type": "children[&5].children[&3].children[&1].class" } } } } } } } } } ]