Сопоставить поле JSON, которое может иметь разные типы с Джексоном? - PullRequest
6 голосов
/ 13 июля 2011

Я получаю JSON от веб-службы и не могу влиять на формат JSON. Приведенный ниже код JSON является лишь примером для иллюстрации проблемы. Поле cars может быть либо объектом, содержащим объекты Car, либо пустой строкой. Если бы я мог изменить веб-сервис, я бы изменил пустую строку на пустой объект, такой как "cars" : {} вместо "cars" : "".

При попытке сопоставить JSON с этим объектом Java:

public class Person {
    public int id;
    public String name;
    public Map<String, Car> cars;
}

Это работает:

{
    "id" : "1234",
    "name" : "John Doe",
    "cars" : {
        "Tesla Model S" : {
            "color" : "silver",
            "buying_date" : "2012-06-01"
        },
        "Toyota Yaris" : {
            "color" : "blue",
            "buying_date" : "2005-01-01"
        }
    }
}

А это не получается:

{
    "id" : "1",
    "name" : "The Dude",
    "cars" : ""
}

Как лучше всего справиться с этим делом в Джексоне? Если есть пустая строка, я бы хотел получить null для поля cars. Я пытался использовать ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, но это не помогло.

1 Ответ

6 голосов
/ 13 июля 2011

Полевые машины могут содержать список объектов Car ... Это работает:

{
    "id" : "1234",
    "name" : "John Doe",
    "cars" : {
        "Tesla Model S" : {
            "color" : "silver",
            "buying_date" : "2012-06-01"
        },
        "Toyota Yaris" : {
            "color" : "blue",
            "buying_date" : "2005-01-01"
        }
    }
}

Значение элемента "cars" не является списком (он же массив).Это объект JSON, который также можно рассматривать как коллекцию типа карты, но это не список.

Итак, чтобы перефразировать проблему, цель состоит в десериализации JSON, который иногда является объектом, а иногда ипустая строка в Java Map.

Чтобы решить эту проблему, я удивлен ACCEPT_EMPTY_STRING_AS_NULL_OBJECT не сработало.Я рекомендую регистрировать проблему на http://jira.codehaus.org/browse/JACKSON.

Вы можете реализовать настраиваемую десериализацию .Ниже приведен пример решения.Если целевая структура данных имеет другие ссылки Map, то это решение необходимо соответствующим образом изменить.

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

import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.ObjectCodec;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.type.TypeReference;

public class Foo
{
  public static void main(String[] args) throws Exception
  {
    SimpleModule module = new SimpleModule("CarsDeserializer", Version.unknownVersion());
    module.addDeserializer(Map.class, new CarsDeserializer());

    ObjectMapper mapper = new ObjectMapper().withModule(module);

    Person person1 = mapper.readValue(new File("input1.json"), Person.class);
    System.out.println(mapper.writeValueAsString(person1));
    // {"id":1234,"name":"John Doe","cars":{"Tesla Model S":{"color":"silver","buying_date":"2012-06-01"},"Toyota Yaris":{"color":"blue","buying_date":"2005-01-01"}}}

    Person person2 = mapper.readValue(new File("input2.json"), Person.class);
    System.out.println(mapper.writeValueAsString(person2));
    // {"id":1,"name":"The Dude","cars":{}}
  }
}

class Person
{
  public int id;
  public String name;
  public Map<String, Car> cars;
}

class Car
{
  public String color;
  public String buying_date;
}

class CarsDeserializer extends JsonDeserializer<Map<String, Car>>
{
  @Override
  public Map<String, Car> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
      JsonProcessingException
  {
    ObjectCodec codec = jp.getCodec();
    JsonNode node = codec.readTree(jp);
    if (!"".equals(node.getTextValue()))
    {
      ObjectMapper mapper = new ObjectMapper();
      return mapper.readValue(node, new TypeReference<Map<String, Car>>() {});
    }
    return new HashMap<String, Car>(); // or return null, if preferred
  }
}
...