Open JiexunLi opened 3 years ago
Its only possible with custom modifies.
package br.com.teste.jolt;
import com.bazaarvoice.jolt.common.Optional;
import com.bazaarvoice.jolt.modifier.function.Function;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class CustomFunctions {
public static final class timestampToDateString extends Function.SingleFunction<String> {
@Override
protected Optional<String> applySingle(Object arg) {
if (arg == null) {
return Optional.empty();
}
Long timestamp = (Long) arg;
Instant instant = Instant.ofEpochMilli(timestamp);
DateTimeFormatter isoOffsetDateTime = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.systemDefault());
return Optional.of(isoOffsetDateTime.format(instant));
}
}
}
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.Math;
import com.bazaarvoice.jolt.modifier.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("timestampToDateString", new CustomFunctions.timestampToDateString());
}
@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);
}
}
}
[{
"operation": "br.com.teste.jolt.CustomModifier$Overwritr",
"spec": {
"timestamp": "=timestampToDateString(@(1,timestamp))"
}
}]
EDIT Now is ok.
@lucioalmeida I Tried your solution but, Chainr is throwing a class not found exception for the custom Overwritr "br.com.teste.jolt.CustomModifier.Overwritr".
Jolt Chainr could not find transform class br.com.teste.jolt.CustomModifier.Overwritr at index:0.
I checked the package names and the class name, they all match. Please suggest
CustomFunctions has a bug. This is ok now
public class CustomFunctions {
public static final class timestampToDateString extends Function.SingleFunction<String> {
@Override
protected Optional<String> applySingle(Object arg) {
if (arg == null) {
return Optional.empty();
}
Long timestamp = (Long) arg;
Instant instant = Instant.ofEpochMilli(timestamp);
DateTimeFormatter isoOffsetDateTime = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.systemDefault());
return Optional.of(isoOffsetDateTime.format(instant));
}
}
}
The class name is ok now
[{
"operation": "br.com.teste.jolt.CustomModifier$Overwritr",
"spec": {
"timestamp": "=timestampToDateString(@(1,timestamp))"
}
}]
That worked great 👍 .
Can I pass any arguments to the SingleFunction such as the date format where my spec can include the target date format. `[{ "operation": "br.com.teste.jolt.CustomModifier$Overwritr", "spec": { "timestamp": "=timestampToDateString(@(1,timestamp, "MM-dd-yyyy hh:mm:ss"))" }
}] `
Is possible with this
package br.com.teste.jolt;
import com.bazaarvoice.jolt.common.Optional;
import com.bazaarvoice.jolt.modifier.function.Function;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
public class CustomFunctions {
public static final class timestampToDateString extends Function.ListFunction {
@Override
protected Optional<Object> applyList(List<Object> argList) {
if (argList.size() < 2) {
return Optional.empty();
}
Long timestamp = (Long) argList.get(0);
String pattern = (String) argList.get(1);
Instant instant = Instant.ofEpochMilli(timestamp);
DateTimeFormatter isoOffsetDateTime = DateTimeFormatter.ofPattern(pattern).withZone(ZoneId.systemDefault());
return Optional.of(isoOffsetDateTime.format(instant));
}
}
}
Spec:
[{
"operation": "br.com.teste.jolt.CustomModifier$Overwritr",
"spec": {
"timestamp": "=timestampToDateString(@(1,timestamp), 'MM-dd-yyyy hh:mm:ss')"
}
}]
Output:
{"timestamp":"06-17-2021 05:16:13"}
EDIT Spec is correct now
Thanks for your swift replies. I tried this but the applyList method was never called.
applySingle method works fine though
Sorry. My spec is wrong
[{
"operation": "br.com.teste.jolt.CustomModifier$Overwritr",
"spec": {
"timestamp": "=timestampToDateString(@(1,timestamp), 'MM-dd-yyyy hh:mm:ss')"
}
}]
@lucioalmeida Awesome, it works now. You saved me a lot of time.
Thank You 👍 :)
Your welcome. Could you close the issue.
I am not able to close it as someone else opened the issue. @JiexunLi Can you please close the issue, if you got what you needed.
@lucioalmeida I followed exact same steps but custom transformer is not getting hit while transform. Anything missing or modification required?
Is there a way to specify something that like
to convert timestamp(long) to a date string?
input: { "timestamp": 1623960973468 }
output: { "timestamp": "2021-06-17T20:16:13.468Z" }