jackson-dataformats-binary icon indicating copy to clipboard operation
jackson-dataformats-binary copied to clipboard

Support work with com.google.protobuf.Struct

Open He-Pin opened this issue 1 month ago • 3 comments

com.google.protobuf.Struct can be used to represent JsonObject.

He-Pin avatar Oct 30 '25 14:10 He-Pin

What would be the benefit here?

We do not want to add more 3rd party dependencies, so I don't think this is something we should do.

cowtowncoder avatar Oct 30 '25 17:10 cowtowncoder

I can convert it from and to json, so does the flatterBuffers.

package com.alibaba.ultramax.mtop.jsonpath;

/**
 * TODO: description of this file
 *
 * @author 虎鸣, [email protected]
 */

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.flatbuffers.FlexBuffers;
import com.google.flatbuffers.FlexBuffersBuilder;

import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Map;

public class FlexBuffersConverter {
    /**
     * 将 Jackson JsonNode 转换为 FlexBuffers 二进制。
     *
     * @param node 要转换的 JsonNode。
     * @return 包含 FlexBuffers 数据的 ByteBuffer。
     */
    public static ByteBuffer convert(JsonNode node) {
        FlexBuffersBuilder builder = new FlexBuffersBuilder();
        buildFromNode(builder, null, node);
        return builder.finish();
    }

    /**
     * 递归函数,根据 JsonNode 类型构建 FlexBuffers。
     *
     * @param builder FlexBuffers 构建器。
     * @param node    当前要处理的 JsonNode。
     */
    private static void buildFromNode(FlexBuffersBuilder builder, String key, JsonNode node) {
        if (node == null || node.isNull()) {
            builder.putNull(key);
        } else if (node.isObject()) {
            buildMapFromNode(builder, key, node);
        } else if (node.isArray()) {
            buildVectorFromNode(builder, key, node);
        } else if (node.isTextual()) {
            builder.putString(key, node.asText());
        } else if (node.isIntegralNumber()) {
            builder.putInt(key, node.asLong());
        } else if (node.isFloatingPointNumber()) {
            builder.putFloat(key, node.asDouble());
        } else if (node.isBoolean()) {
            builder.putBoolean(key, node.asBoolean());
        } else {
            // 对于其他未知类型,作为 null 处理
            builder.putNull(key);
        }
    }

    private static void buildMapFromNode(FlexBuffersBuilder builder, final String key, JsonNode objectNode) {
        int mapStart = builder.startMap();
        Iterator<Map.Entry<String, JsonNode>> fields = objectNode.fields();
        while (fields.hasNext()) {
            Map.Entry<String, JsonNode> field = fields.next();
            buildFromNode(builder, field.getKey(), field.getValue());
        }
        builder.endMap(key, mapStart);
    }

    private static void buildVectorFromNode(FlexBuffersBuilder builder, String key, JsonNode arrayNode) {
        int vecStart = builder.startVector();
        for (JsonNode elementNode : arrayNode) {
            // 递归构建数组中的每个元素
            buildFromNode(builder, null, elementNode);
        }
        builder.endVector(key, vecStart, false, false);
    }

    /**
     * 将 FlexBuffers 二进制转换为 Jackson JsonNode。
     *
     * @param buffer 包含 FlexBuffers 数据的 ByteBuffer。
     * @return 转换后的 JsonNode。
     */
    public static JsonNode convert(ByteBuffer buffer) {
        FlexBuffers.Reference ref = FlexBuffers.getRoot(buffer);
        return buildJsonNodeFromRef(ref);
    }

    /**
     * 递归函数,根据 FlexBuffers 引用类型构建 JsonNode。
     *
     * @param ref 当前要处理的 FlexBuffers 引用。
     * @return 构建好的 JsonNode。
     */
    private static JsonNode buildJsonNodeFromRef(FlexBuffers.Reference ref) {
        if (ref.isNull()) {
            return JsonNodeFactory.instance.nullNode();
        } else if (ref.isMap()) {
            ObjectNode objectNode = JsonNodeFactory.instance.objectNode();
            FlexBuffers.Map map = ref.asMap();
            FlexBuffers.KeyVector keys = map.keys();
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.get(i).toString();
                // 递归构建 value
                objectNode.set(key, buildJsonNodeFromRef(map.get(key)));
            }
            return objectNode;
        } else if (ref.isVector()) {
            ArrayNode arrayNode = JsonNodeFactory.instance.arrayNode();
            FlexBuffers.Vector vector = ref.asVector();
            for (int i = 0; i < vector.size(); i++) {
                // 递归构建数组中的每个元素
                arrayNode.add(buildJsonNodeFromRef(vector.get(i)));
            }
            return arrayNode;
        } else if (ref.isString()) {
            return JsonNodeFactory.instance.textNode(ref.asString());
        } else if (ref.isInt()) {
            final long value = ref.asLong();
            if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
                return JsonNodeFactory.instance.numberNode((int) value);
            } else {
                return JsonNodeFactory.instance.numberNode(value);
            }
        } else if (ref.isFloat()) {
            return JsonNodeFactory.instance.numberNode(ref.asFloat());
        } else if (ref.isBoolean()) {
            return JsonNodeFactory.instance.booleanNode(ref.asBoolean());
        }
        return JsonNodeFactory.instance.nullNode();
    }
}


He-Pin avatar Oct 30 '25 17:10 He-Pin

jackson-dataformat-protobuf is for streaming (incremental) encoding/decoding of protobuf-encoded data so bit different.

This sounds like an idea for an optional "jackson-datatype-xxx" module that helps Jackson work with 3rd party value types. Maybe under https://github.com/FasterXML/jackson-dataformats-misc.

cowtowncoder avatar Oct 30 '25 21:10 cowtowncoder