2017-05-08 70 views
0

親愛的人們我需要你的建議。將json嵌入式節點反序列化爲值使用Jackson的地圖

我是,試圖實現自定義Jackson解串器的地圖,但有困難。作爲輸入數據,我有以下的JSON:

{ 
    "someMap": { 
     "one_value": "1", 
     "another_value: "2" 
    }, 
    "anotherMap": "{\"100000000\": 360000,\"100000048\": 172800,\"100000036\": 129600,\"100000024\": 86400,\"100000012\": 43200}" 
} 

正如你可以在它節點值內JSON地圖(我做那樣的意向第二種情況中看到,因爲我想要更換。來自env變量的值:"anotherMap": "${SOME_MAP:-{\"100000000\": 360000,\"100000048\": 172800,\"100000036\": 129600,\"100000024\": 86400,\"100000012\": 43200}}")。據我瞭解,我必須區分這兩個地圖反序列化流程。所以對於第一個映射,我需要使用默認的一個映射反序列化器作爲第二個自定義映射器,以便從值中正確解析映射。目前,我寫的代碼來做到這一點:

// invokation code 
new ObjectMapper().registerModule(new ConfigModule()).readValue(is, ConfigModuleTestConfigWrapper.class); 

// module code 
public class ConfigModule extends SimpleModule { 

@Override 
public void setupModule(SetupContext context) { 
    super.setupModule(context); 
    context.addDeserializers(new Deserializers.Base() { 

      @Override 
      public JsonDeserializer<?> findMapDeserializer(MapType type, DeserializationConfig config, BeanDescription beanDesc, 
                 KeyDeserializer keyDeserializer, TypeDeserializer elementTypeDeserializer, 
                 JsonDeserializer<?> elementDeserializer) throws JsonMappingException { 
       return new MapPropertyDeserializer(type); 
      } 
    }); 
} 

private static class MapPropertyDeserializer extends StdScalarDeserializer<Map<String, Integer>> { 
    MapPropertyDeserializer(MapType type) { 
     super(type); 
    } 
    @Override 
    public Map<String, Integer> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 
     JsonNode node = p.readValueAsTree(); 
     if (node == null || node.isContainerNode()) { 
       // return <default jackson deserializer> 
     } 
     System.out.println("isContainerNode ?: " + node.isContainerNode()); 
     System.out.println("isValueNode ?: " + node.isValueNode()); 
     // some parsing flow goes below 
     JsonNode valueNode = node.get(1); 
     valueNode.asText(); 
     return new HashMap<>(); 
    } 
} 

// bean description 
@JsonIgnoreProperties 
public class ConfigSubstitutorModuleTestConfigWrapper { 

    private final Map<String, String> someMap; 
    private final Map<String, Integer> anotherMap; 

    @JsonCreator 
    public ConfigSubstitutorModuleTestConfigWrapper(
      @JsonProperty("someMap") Map<String, String> someMap, 
      @JsonProperty("anotherMap") Map<String, Integer> anotherMap 

) { 
     this.someMap = someMap; 
     this.anotherMap = anotherMap; 
    } 

    public Map<String, String> getSomeMap() { 
     return someMap; 
    } 
    public Map<String, Integer> getAnotherMap() { 
     return anotherMap; 
    } 
} 

的問題是(我的理解:)),我不知道如何從反序列化方法返回默認地圖解串器。

有沒有人有任何線索我可以在那裏做到達到預期的目標?

+0

你不能修復而產生的內部JSON這個醜陋的JSON字符串的一部分?這是看起來破碎的部分。 –

+0

我不能那樣做。這裏的想法是用環境變量中的值替換那個醜陋的json,比如''anotherMap「:」$ {SOME_MAP: - {\「100000000 \」:360000,\「100000048 \」:172800,\「100000036 \」: 129600,\「100000024 \」:86400,\「100000012 \」:43200}}「'。 所以我按意願去做。 –

+0

可能的重複http://stackoverflow.com/questions/18313323/how-do-i-call-the-default-deserializer-from-a-custom-deserializer-in-jackson –

回答

0

最終接受了解決方案解決它:

1)創建解串器類:

/** 
* The target of that deserializer is to do two-step deserialization. 
* At first it just reads string and then does second deserialization in the proper {@link Map} type once string substitution done. 
* <p> 
* Note! In order to get object mapper reference you have to set it first on object mapper initialization stage: 
* </p> 
* <pre> 
*  objectMapper.setInjectableValues(new InjectableValues.Std().addValue(OBJECT_MAPPER_VALUE_ID, objectMapper)); 
* </pre> 
*/ 
public class ValueAsMapDeserializer extends JsonDeserializer<Map> implements ContextualDeserializer { 
    public static final String OBJECT_MAPPER_VALUE_ID = "objectMapper"; 
    static final String VALUE_PREFIX = "$|"; 
    static final String VALUE_SUFFIX = "|"; 

    private JavaType keyType; 
    private JavaType valueType; 

    @Override 
    public JsonDeserializer<?> createContextual(final DeserializationContext ctxt, 
               final BeanProperty property) throws JsonMappingException { 
     JavaType filedType = property.getType(); 
     this.keyType = filedType.getKeyType(); 
     this.valueType = filedType.getContentType(); 
     return this; 
    } 

    @Override 
    public Map deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { 
     // Can't use constructor init there because of intention to use that deserializer using annotation 
     // Also such tricky thing as 'injectable values' was used cause of no way to get the reference to object mapper from deserialization context out of the box 
     ObjectMapper objectMapper = (ObjectMapper) ctxt.findInjectableValue(OBJECT_MAPPER_VALUE_ID, null, null); 
     final Optional<String> substitutedValue = Substitutor.create(jp, VALUE_PREFIX, VALUE_SUFFIX).substitute(); 
     MapType mapType = objectMapper.getTypeFactory().constructMapType(Map.class, keyType, valueType); 
     return objectMapper.readValue(substitutedValue.orElseThrow(() -> new RuntimeException("Failed to parse the value as map")), mapType); 
    } 
} 

2)馬克bean字段使用該解串器:

@JsonDeserialize(using = ValueAsMapDeserializer.class) 
private final Map<String, Integer> anotherMap; 
0

嘗試分兩步進行反序列化:

package stack43844461; 

import java.io.IOException; 
import java.util.Map; 

import org.junit.Test; 

import com.fasterxml.jackson.core.JsonParseException; 
import com.fasterxml.jackson.databind.JsonMappingException; 
import com.fasterxml.jackson.databind.ObjectMapper; 

public class HowToConvertJsonStringToMap { 

    @Test 
    public void json() throws JsonParseException, JsonMappingException, IOException { 

     String jsonInString = "{\"someMap\":" 
         + " {\"one_value\": \"1\"," 
         + "\"another_value\": \"2\"}," 
         + "\"anotherMap\": " 
         + "\"{\\\"100000000\\\": 360000," 
         + "\\\"100000048\\\": 172800," 
         + "\\\"100000036\\\": 129600," 
         + "\\\"100000024\\\": 86400," 
         + "\\\"100000012\\\": 43200}\"}"; 

     ObjectMapper mapper = new ObjectMapper(); 
     // Step 1: Read everything into one object. 
     Map<String, Object> all = mapper.readValue(jsonInString, Map.class); 
     // Step 2: Get your "normal" data into one object 
     Map<String, Object> someMap=(Map<String, Object>) all.get("someMap"); 
     // Step 3: Get your "embedded" data from your object 
     String anotherMapStr = (String) all.get("anotherMap"); 
     // Step 4: Deserialize embedded data 
     Map<String, Object> anotherMap = mapper.readValue(anotherMapStr, Map.class); 
     System.out.println(anotherMap); 
     System.out.println(someMap); 
    } 

} 

打印:

{100000000=360000, 100000048=172800, 100000036=129600, 100000024=86400, 100000012=43200} 
{one_value=1, another_value=2} 
+0

是的,它可能會適用於我的情況。但我必須使用將爲objectMapper註冊的模塊,所以我需要在反序列化程序中執行此操作,而不是使用'jsonInString'我在類型爲Map的POJO中包含字段,並且不知道像anotherMap這樣的確切字段名稱, –

相關問題