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

时间:2022-03-28 20:27:11

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

我有一个Java项目,它当前将一个映射返回给Camel,没有任何嵌套的键,值对,并且Jackson库使用Jackson库将它编组到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/jackson/convert-java-map-to-from-json-using-jackson-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/jackson/convert-java-map-to-from-json-using-jackson-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

即使我尝试使用grins,我也遇到了一个来自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.html but 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,它将属性文件加载到Map中:

package sample;

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

import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.codehaus.jackson.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”,但稍后会将其更改为也从属性文件中提取。

1 个解决方案

#1


6  

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/jackson-annotations/wiki/Jackson-Annotations

我今天一直在与这个斗争,你的问题激励我做出血腥的工作:D注释在这里:https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations

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

请参阅此处的JUnit测试:https://gist.github.com/TomDemeranville/7009250

#1


6  

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/jackson-annotations/wiki/Jackson-Annotations

我今天一直在与这个斗争,你的问题激励我做出血腥的工作:D注释在这里:https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations

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

请参阅此处的JUnit测试:https://gist.github.com/TomDemeranville/7009250