Джексон ObjectMapper от MongoDB BSON - PullRequest
       11

Джексон ObjectMapper от MongoDB BSON

0 голосов
/ 26 февраля 2019

Я получил JSON, который сериализовал в MongoDB BasicDBObject и вставил в БД:

    String serialized = "";
    try {
        serialized = OBJECT_MAPPER.writeValueAsString(customEx.getOut().getBody());
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }

    collection.update(upsertQuery, BasicDBObject.parse(serialized), true, false);

При чтении DBObject из БД я хочу преобразовать его в POJO с помощью ObjectMappers ''readValue 'с данным классом:

    public static <T> T fromDB(DBObject o, Class<T> clazz) {
    try {
        return OBJECT_MAPPER.readValue(o.toString(), clazz);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

Класс, в который я хочу преобразовать его, генерируется из xsd sheme и также содержит временные метки / длинные значения следующим образом:

@XmlRootElement(name = "ItemType")
public class ItemType {

@XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar date;
[...]

Однакоэто работало нормально для более старых версий Java MongoDB.Теперь длинные значения сериализуются как BSON, выглядя так:

"date": {
    "$numberLong": "1551172199214"
}

Когда я пытаюсь десериализовать это с помощью jacksons ObjectMapper, я получаю

Невозможно десериализовать экземпляр javax.xml.datatype.XMLGregorianCalendar изТокен START_OBJECT

Причина этого мне ясна, поскольку длинное значение находится в собственном объекте стиля BSON.

До сих пор я уже пытался использовать BsonDocument, например:

public static <T> T fromDB(DBObject o, Class<T> clazz) {
    try {
        BsonDocument parse = BsonDocument.parse(o.toString());
        return OBJECT_MAPPER.readValue(parse.toJson(), clazz);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

Но это по-прежнему не конвертирует части BSON в JSON.

Есть ли способ десериализации BSON в данный класс с использованием jacksons ObjectMapper?Или просто преобразовать его в DBObject без использования частей BSON?

Ответы [ 2 ]

0 голосов
/ 26 февраля 2019

Если поле календаря обернуто, нам нужно его развернуть.Мы можем расширить уже реализованные CoreXMLDeserializers.GregorianCalendarDeserializer:

class XmlGregorianCalendarDeserializer extends CoreXMLDeserializers.GregorianCalendarDeserializer {

    @Override
    public XMLGregorianCalendar deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException {
        jp.nextToken(); // Skip FIELD_NAME
        jp.nextToken(); // Skip VALUE_STRING

        XMLGregorianCalendar calendar = super.deserialize(jp, ctxt);

        jp.nextToken(); // Skip END_OBJECT

        return calendar;
    }
}

Если XMLGregorianCalendar всегда упакован, мы можем зарегистрировать этот десериализатор, используя SimpleModule.См. Пример ниже:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ext.CoreXMLDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import javax.xml.datatype.XMLGregorianCalendar;

public class Test {

    public static void main(String[] args) throws Exception {
        SimpleModule wrappedCalendarModule = new SimpleModule();
        wrappedCalendarModule.addDeserializer(XMLGregorianCalendar.class, new XmlGregorianCalendarDeserializer());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(wrappedCalendarModule);
        String json = "{\n"
            + "   \"date\":{\n"
            + "      \"$numberLong\":\"1551172199214\"\n"
            + "   },\n"
            + "   \"name\":\"Rick\"\n"
            + "}";
        System.out.println(mapper.readValue(json, Wrapper.class));
    }
}

class Wrapper {

    private XMLGregorianCalendar date;
    private String name;

    // getters, setters, toString
}

Над отпечатками кода:

Wrapper{date=2019-02-26T09:09:59.214Z, name='Rick'}
0 голосов
/ 26 февраля 2019

Похоже, вам нужно настроить ObjectMapper для десериализации XMLGregorianCalendar по-другому.

public class XMLGregorianCalendarDeserializer extends JsonDeserializer<XMLGregorianCalendar> {
private ObjectMapper objectMapper = new ObjectMapper();

@Override
public XMLGregorianCalendar deserialize(
    JsonParser jsonParser, DeserializationContext deserializationContext)
    throws IOException, JsonProcessingException {
  Map<String, String> bsonStringAsMap =
      objectMapper.readValue(
          jsonParser.readValueAsTree().toString(), new TypeReference<Map<String, String>>() {});

  String timestampString = bsonStringAsMap.get("$numberLong");
  if (timestampString != null && !timestampString.isEmpty()) {
    long timestamp = Long.parseLong(timestampString);
    Date date = new Date(timestamp);
    GregorianCalendar gregorianCalendar = new GregorianCalendar();
    gregorianCalendar.setTime(date);
    return DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar);
  }
  return null;
  }
}

Затем вы можете аннотировать поле с этим и десериализовать, как вы это делали:

@JsonDeserialize(using=XMLGregorianCalendarDeserializer.class)
@XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar date;
...