Java 如何使用 Camel 和 Jackson 库将嵌套的键值对编组到 JSON 中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19389696/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 16:43:08  来源:igfitidea点击:

How do I marshall nested key,value pairs into JSON with Camel and Hymanson library?

javajsonspringHymansonapache-camel

提问by erj2code

I have a Java project that currently returns a map to Camel without any nested key,value pairs, and the Hymanson library marshalls it just fine into JSON using the Hymanson library.

我有一个 Java 项目,它当前返回一个没有任何嵌套键值对的 Camel 映射,并且 Hymanson 库使用 Hymanson 库将它编组到 JSON 中。

For example if I put the following two key,values into a demoMap:

例如,如果我将以下两个键值放入 demoMap:

Map<String,String> demoMap = new TreeMap<String,String>
demoMap.put("key1","5");
demoMap.put("key2","10");

I get the following JSON:

我得到以下 JSON:

{"key1":"5","key2":"10"}

However, now some of my key,value entries will have an optional visibility that I need to put as a nested key value in my map. Any key,values that don't have an optional visibility will use the default. So, for example if I specify visibilities for key1, and key2, but not key3 I want to get JSON out that looks like this:

但是,现在我的一些键值条目将具有可选的可见性,我需要将其作为嵌套键值放入我的地图中。任何没有可选可见性的键值都将使用默认值。因此,例如,如果我为 key1 和 key2 而不是 key3 指定了可见性,我想得到如下所示的 JSON:

{"key1":"5",
 "key2":"10",
 "key3":"17",
 "visibility" : { "key1": "a&b&!c", "key2": "a&b", "_default": "a" }
}

How can I get Camel to marshall a Java object with nested key,value pairs? I'm a visual learner, so a simple example would be helpful.

如何让 Camel 使用嵌套的键值对编组 Java 对象?我是一个视觉学习者,所以一个简单的例子会有所帮助。

I tried changing my Map to have a value as an object i.e.,:

我尝试将我的 Map 更改为具有作为对象的值,即:

Map<String,Object> demoMap = new TreeMap<String,Object>

and then tried adding nested key,values for some keys with an ArrayList using http://examples.javacodegeeks.com/core-java/json/Hymanson/convert-java-map-to-from-json-using-Hymanson-example/for reference, but realized that this just gives me a bunch of nested values under a key, not a bunch of nested key,value pairs.

然后尝试使用http://examples.javacodegeeks.com/core-java/json/Hymanson/convert-java-map-to-from-json-using-Hymanson-example为一些带有 ArrayList 的键添加嵌套键值/作为参考,但意识到这只是给了我一堆键下的嵌套值,而不是一堆嵌套的键值对。

Even when I tried it for grins, I got an error from the Camel processor with a java.lang.ClassCastException stating java.util.ArrayList cannot be cast to java.lang.String

即使当我尝试笑笑时,我也收到了来自 Camel 处理器的错误,其中包含 java.lang.ClassCastException 说明无法将 java.util.ArrayList 转换为 java.lang.String

And similarly when I tried to nest a Map inside my demoMap I got this ClassCastException:

同样,当我试图在我的 demoMap 中嵌套一个 Map 时,我得到了这个 ClassCastException:

3244 [hello.world.request.timer] ERROR org.apache.camel.processor.DefaultErrorHandler  - Failed delivery for exchangeId: e6518e39-89b7-435e-96d9-ce26811ac67e. Exhausted after delivery attempt: 1 caught: java.lang.ClassCastException: java.util.HashMap cannot be cast to java.lang.String

So I know how NOT to do it. :-/

所以我知道如何不这样做。:-/

I re-read the Camel JSON documentation at http://camel.apache.org/json.htmlbut as of this writing it doesn't specify an example with nested key,value pairs.

我在http://camel.apache.org/json.html重新阅读了 Camel JSON 文档,但在撰写本文时,它没有指定带有嵌套键值对的示例。

UPDATE: Based on feedback from Tom I created two Maps i.e.,

更新:根据汤姆的反馈,我创建了两个地图,即,

Map<String,String> keyvalues = new TreeMap<String,String>();
Map<String,String> visibility = new TreeMap<String,String>();

Here is my class which I call SensorGenerator that loads a properties file into a Map:

这是我称之为 SensorGenerator 的类,它将属性文件加载到地图中:

package sample;

import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;

import org.codehaus.Hymanson.annotate.JsonAnyGetter;
import org.codehaus.Hymanson.annotate.JsonProperty;

public class SensorGenerator {

    private Properties sourceProperties;

    // create a map of sensor keyvalues, and a map of sensor visibility
    Map<String,String> keyvalues = new TreeMap<String,String>();
    @JsonProperty
    Map<String,String> visibility = new TreeMap<String,String>();

    @JsonAnyGetter
    public Map<String, String> getKeyvalues() { 

        for (Object key : sourceProperties.keySet()) {

            // Separate out each of the field:datatype:visibility tuples as an entry in the
            // values array
            String[] values = sourceProperties.getProperty((String) key).split(
                    ",");
            // split the key between 'sensor' and the 'number' Ex: sensor1 -> sensor,1
            String[] keyArray = key.toString().split("(?<=([a-zA-Z]++))");
            String keyNumber = keyArray[1]; // grab the number to append for each sensor

            // define string buffer that appends sensor number for each sensor's
            // keys.  Ex: sensor1 would have s1make, s1makeDataType, etc.
            StringBuffer sensorNumberStringBuffer = new StringBuffer();
            sensorNumberStringBuffer.append("s");
            sensorNumberStringBuffer.append(keyNumber);

            // make, its data type, and visibility (with s# prefix)
            StringBuffer makeStringBuffer = new StringBuffer();
            makeStringBuffer.append(sensorNumberStringBuffer);
            makeStringBuffer.append("make");
            StringBuffer makeDataTypeStringBuffer = new StringBuffer();
            makeDataTypeStringBuffer.append(sensorNumberStringBuffer);
            makeDataTypeStringBuffer.append("makeDataType");
            StringBuffer makeVizStringBuffer = new StringBuffer();
            makeVizStringBuffer.append(sensorNumberStringBuffer);
            makeVizStringBuffer.append("makeViz");

            // model, its data type, and visibility (with s# prefix)
            StringBuffer modelStringBuffer = new StringBuffer();
            modelStringBuffer.append(sensorNumberStringBuffer);
            modelStringBuffer.append("model");
            StringBuffer modelDataTypeStringBuffer = new StringBuffer();
            modelDataTypeStringBuffer.append(sensorNumberStringBuffer);
            modelDataTypeStringBuffer.append("modelDataType");
            StringBuffer modelVizStringBuffer = new StringBuffer();
            modelVizStringBuffer.append(sensorNumberStringBuffer);
            modelVizStringBuffer.append("modelViz");

            // serialNumber, its data type, and visibility (with s# prefix)
            StringBuffer serialNumberStringBuffer = new StringBuffer();
            serialNumberStringBuffer.append(sensorNumberStringBuffer);
            serialNumberStringBuffer.append("serialNumber");
            StringBuffer serialNumberDataTypeStringBuffer = new StringBuffer();
            serialNumberDataTypeStringBuffer.append(sensorNumberStringBuffer);
            serialNumberDataTypeStringBuffer.append("serialNumberDataType");
            StringBuffer serialNumberVizStringBuffer = new StringBuffer();
            serialNumberVizStringBuffer.append(sensorNumberStringBuffer);
            serialNumberVizStringBuffer.append("serialNumberViz");

            // sensorType, its data type, and visibility (with s# prefix)
            StringBuffer sensorTypeStringBuffer = new StringBuffer();
            sensorTypeStringBuffer.append(sensorNumberStringBuffer);
            sensorTypeStringBuffer.append("sensorType");
            StringBuffer sensorTypeDataTypeStringBuffer = new StringBuffer();
            sensorTypeDataTypeStringBuffer.append(sensorNumberStringBuffer);
            sensorTypeDataTypeStringBuffer.append("sensorTypeDataType");
            StringBuffer sensorTypeVizStringBuffer = new StringBuffer();
            sensorTypeVizStringBuffer.append(sensorNumberStringBuffer);
            sensorTypeVizStringBuffer.append("sensorTypeViz");

            //  put all the field:datatype keyvalues for this sensor in the keyvalues map
            //  and visibilities in the visibility map

            // make, data type, and visibility
            keyvalues.put(makeStringBuffer.toString(), values[0].split(":")[0]);
            keyvalues.put(makeDataTypeStringBuffer.toString(), values[0].split(":")[1]);
            visibility.put(makeVizStringBuffer.toString(), values[0].split(":")[2]);

            // model, data type, and visibility
            keyvalues.put(modelStringBuffer.toString(), values[1].split(":")[0]);
            keyvalues.put(modelDataTypeStringBuffer.toString(), values[1].split(":")[1]);
            visibility.put(modelVizStringBuffer.toString(), values[1].split(":")[2]);

            // serialNumber, data type, and visibility
            keyvalues.put(serialNumberStringBuffer.toString(), values[2].split(":")[0]);
            keyvalues.put(serialNumberDataTypeStringBuffer.toString(), values[2].split(":")[1]);
            visibility.put(serialNumberVizStringBuffer.toString(), values[2].split(":")[2]);

            // sensorType, data type, and visibility
            keyvalues.put(sensorTypeStringBuffer.toString(), values[3].split(":")[0]);
            keyvalues.put(sensorTypeDataTypeStringBuffer.toString(), values[3].split(":")[1]);
            visibility.put(sensorTypeVizStringBuffer.toString(), values[3].split(":")[2]);

            // add in default visibility
            visibility.put("_default", "a");

        }
        return keyvalues;
    }

    public void setSourceProperties(Properties properties) {
        this.sourceProperties = properties;
    }

}

Right now I just hardcoded the default visibility to "a", but will change that later to also be pulled from a properties file.

现在我只是将默认可见性硬编码为“a”,但稍后会将其更改为也从属性文件中提取。

采纳答案by tom

Your structure is a more than a map. It's two maps that are serialised differently. One way to represent this is:

您的结构不仅仅是一张地图。这是两个序列化不同的地图。表示这一点的一种方法是:

public class Whatever{
  Map<String,String> keyvalues;
  Map<String,String> visibility;
}

What you'll end up with is this, which although represents the data is far from ideal:

你最终会得到这个,虽然它代表的数据远非理想:

{
 "keyvalues" : { "key1": "5", "key2": "10", "key3": "17"},
 "visibility" : { "key1": "a&b&!c", "key2": "a&b", "_default": "a" }
}

To get what you want, use @JsonAnyGetter. Something like this (it could be made much easier to use):

要获得您想要的东西,请使用@JsonAnyGetter。像这样的东西(它可以更容易使用):

public class Whatever{
    Map<String,String> keyvalues = new TreeMap<String,String>();
    @JsonProperty
    Map<String,String> visibility = new TreeMap<String,String>();

    @JsonAnyGetter
    public Map<String, String> getKeyvalues() {
        return keyvalues;
    }
}

which produces:

它产生:

           {"visibility":{"key1":"a&b&!c","key2":"a&b"},"key1":"5","key2":"10"}

I've been battling this today and your question inspired me to make it bloody work :D The annotations are here: https://github.com/FasterXML/Hymanson-annotations/wiki/Hymanson-Annotations

我今天一直在与这个问题作斗争,你的问题激发了我的灵感:D 注释在这里:https: //github.com/FasterXML/Hymanson-annotations/wiki/Hymanson-Annotations

See JUnit test here: https://gist.github.com/TomDemeranville/7009250

在此处查看 JUnit 测试:https: //gist.github.com/TomDemeranville/7009250