Skip to content

Instantly share code, notes, and snippets.

@Garciat
Last active January 25, 2026 11:31
Show Gist options
  • Select an option

  • Save Garciat/f19938419e53d72d42ce79c839ab9f3d to your computer and use it in GitHub Desktop.

Select an option

Save Garciat/f19938419e53d72d42ce79c839ab9f3d to your computer and use it in GitHub Desktop.
package com.uber.money.caribbean.paymentprofilev2.spring;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.DoubleNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.LongNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.uber.money.caribbean.paymentprofilev2.entities.index.PaymentProfileIndexingConfig;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class IndexingModule {
@ConfigurationProperties(prefix = "indexing")
@Bean
public Map<String, Object> indexingRawStructure() {
return new HashMap<>();
}
@Bean
public PaymentProfileIndexingConfig indexingConfig(Map<String, Object> indexingRawStructure)
throws JsonProcessingException {
Object root = indexingRawStructure.get("indexingRawStructure");
var mapper = new ObjectMapper();
var parser = new SpringPropertiesToJson<>(new JacksonJsonConstructor(JsonNodeFactory.instance));
var tree = parser.parseSpringProperties(root);
return mapper.treeToValue(tree, PaymentProfileIndexingConfig.class);
}
interface JsonConstructor<Element> {
Element makeNull();
Element makeString(String value);
Element makeNumber(Long value);
Element makeNumber(Double value);
Element makeBoolean(boolean value);
Element makeArray(List<Element> value);
Element makeObject(Map<String, Element> value);
}
@RequiredArgsConstructor
public static class JacksonJsonConstructor implements JsonConstructor<JsonNode> {
private final JsonNodeFactory nodeFactory;
@Override
public JsonNode makeNull() {
return NullNode.getInstance();
}
@Override
public JsonNode makeString(String value) {
return new TextNode(value);
}
@Override
public JsonNode makeNumber(Long value) {
return new LongNode(value);
}
@Override
public JsonNode makeNumber(Double value) {
return new DoubleNode(value);
}
@Override
public JsonNode makeBoolean(boolean value) {
return BooleanNode.valueOf(value);
}
@Override
public JsonNode makeArray(List<JsonNode> value) {
return new ArrayNode(nodeFactory, value);
}
@Override
public JsonNode makeObject(Map<String, JsonNode> value) {
return new ObjectNode(nodeFactory, value);
}
}
@RequiredArgsConstructor
public static class SpringPropertiesToJson<Element> {
private final JsonConstructor<Element> json;
public Element parseSpringProperties(Object root) {
return parse(root);
}
// Recursion entry-point
private Element parse(Object node) {
return fullParser()
.apply(node)
.orElseThrow(() -> new IllegalStateException("Could not parse: " + node.getClass()));
}
private Partial<Object, Element> fullParser() {
return asValue()
.flatMap(
nullParser()
.orElse(booleanParser())
.orElse(longParser())
.orElse(doubleParser())
.orElse(stringParser()))
.orElse(asContainer().flatMap(arrayParser().orElse(objectParser())));
}
private Partial<Object, String> asValue() {
return Partial.tryCast(String.class);
}
private Partial<Object, Map<String, Object>> asContainer() {
return Partial.tryCast(Map.class).map(m -> (Map<String, Object>) m);
}
private Partial<String, Element> nullParser() {
return Partial.match("null").replaceLazy(json::makeNull);
}
private Partial<String, Element> stringParser() {
return Partial.of(json::makeString);
}
private Partial<String, Element> longParser() {
return Partial.trying((String s) -> Long.parseLong(s), NumberFormatException.class)
.map(json::makeNumber);
}
private Partial<String, Element> doubleParser() {
return Partial.trying(Double::parseDouble, NumberFormatException.class)
.map(json::makeNumber);
}
private Partial<String, Element> booleanParser() {
return Partial.match("true").replace(true)
.orElse(Partial.match("false").replace(false))
.map(json::makeBoolean);
}
private Partial<Map<String, Object>, Element> arrayParser() {
return Partial.of(this::readAsList).map(this::recurse).map(json::makeArray);
}
private Partial<Map<String, Object>, Element> objectParser() {
return Partial.of(this::readAsMap).map(this::recurse).map(json::makeObject);
}
private List<Element> recurse(List<Object> items) {
return items.stream().map(this::parse).collect(Collectors.toList());
}
private Map<String, Element> recurse(Map<String, Object> items) {
return items.entrySet().stream().collect(Collectors.toMap(k -> k.getKey(), v -> parse(v.getValue())));
}
// Tries to parse shape: {"0": x, "1": y, "2": z, ...}
private Optional<List<Object>> readAsList(Map<String, Object> map) {
var output = new ArrayList<>();
for (int i = 0; i < map.size(); i++) {
var item = map.get(Integer.toString(i));
if (item == null) {
return Optional.empty();
} else {
output.add(item);
}
}
return Optional.of(output);
}
// Tries to parse shape: {"0": x, "1": y, "2": z, ...}
private Optional<List<Object>> readAsListFunctional(Map<String, Object> map) {
return Stream.iterate(0, n -> n + 1)
.limit(map.size())
.map(i -> Partial.lookup(Integer.toString(i)).map(Arrays::asList))
.reduce((top, next) -> Partial.join(top, next, ListUnion::apply))
.orElseGet(Partial::fail)
.apply(map);
}
private Optional<Map<String, Object>> readAsMap(Map<String, Object> map) {
return Optional.of(map);
}
}
interface Partial<T, R> extends Function<T, Optional<R>> {
Optional<R> apply(T input);
default <S> Partial<T, S> map(Function<R, S> f) {
return input -> apply(input).map(f);
}
default <S> Partial<T, S> replace(S value) {
return map(ignored -> value);
}
default <S> Partial<T, S> replaceLazy(Supplier<S> value) {
return map(ignored -> value.get());
}
default <S> Partial<T, S> flatMap(Partial<R, S> next) {
return input -> apply(input).flatMap(next);
}
default Partial<T, R> orElse(Partial<T, R> fallback) {
return input -> {
var first = apply(input);
if (first.isPresent()) {
return first;
} else {
return fallback.apply(input);
}
};
}
static <T, R> Partial<T, R> of(Partial<T, R> it) {
return it;
}
static <T, R> Partial<T, R> of(Function<T, R> it) {
return input -> Optional.ofNullable(it.apply(input));
}
static <R> Partial<Object, R> tryCast(Class<R> target) {
return trying(target::cast, ClassCastException.class);
}
static <T, R, E extends Throwable> Partial<T, R> trying(FunctionEx<T, R, E> f, Class<E> expected) {
return input -> {
try {
return Optional.of(f.apply(input));
} catch (Throwable ex){
if (expected.isInstance(ex)) {
return Optional.empty();
} else {
throw new RuntimeException("Unexpected exception type", ex);
}
}
};
}
static <T> Partial<T, T> match(T expected) {
return input -> {
if (expected.equals(input)) {
return Optional.of(input);
} else {
return Optional.empty();
}
};
}
static <T, R> Partial<Map<T, R>, R> lookup(T key) {
return input -> Optional.ofNullable(input.get(key));
}
static <T, R, S> Partial<T, S> join(Partial<T, R> left, Partial<T, R> right, BiFunction<R, R, S> f) {
return input -> left.apply(input).flatMap(r1 -> right.apply(input).map(r2 -> f.apply(r1, r2)));
}
static <T> Partial<T, T> identity() {
return Optional::of;
}
static <T, R> Partial<T, R> fail() {
return input -> Optional.empty();
}
}
interface FunctionEx<T, R, E extends Throwable> {
R apply(T input) throws E;
}
public static class ListUnion {
public static <T> List<T> apply(List<T> xs, List<T> ys) {
return Stream.concat(xs.stream(), ys.stream()).collect(Collectors.toList());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment