get jackson object mapper to support dates as elasticsearch expects them

This commit is contained in:
kimchy 2010-03-09 17:09:07 +02:00
parent 58319b1113
commit baaac70da5
8 changed files with 143 additions and 22 deletions

View File

@ -55,7 +55,7 @@ public class JacksonTypesBenchmark {
public JacksonTypesBenchmark(String jsonString) throws IOException {
Preconditions.checkNotNull(jsonString, "jsonString must have a value");
this.jsonString = jsonString;
this.objectMapper = newObjectMapper();
this.objectMapper = defaultObjectMapper();
this.factor = 10;
this.cycles = 10000;

View File

@ -83,8 +83,8 @@ public class JsonDateFieldMapper extends JsonNumberFieldMapper<Long> {
float boost, boolean omitNorms, boolean omitTermFreqAndPositions,
String nullValue) {
super(names, precisionStep, index, store, boost, omitNorms, omitTermFreqAndPositions,
new NamedAnalyzer("_date/" + precisionStep, new NumericDateAnalyzer(precisionStep, dateTimeFormatter.formatter())),
new NamedAnalyzer("_date/max", new NumericDateAnalyzer(Integer.MAX_VALUE, dateTimeFormatter.formatter())));
new NamedAnalyzer("_date/" + precisionStep, new NumericDateAnalyzer(precisionStep, dateTimeFormatter.parser())),
new NamedAnalyzer("_date/max", new NumericDateAnalyzer(Integer.MAX_VALUE, dateTimeFormatter.parser())));
this.dateTimeFormatter = dateTimeFormatter;
this.nullValue = nullValue;
}
@ -102,11 +102,11 @@ public class JsonDateFieldMapper extends JsonNumberFieldMapper<Long> {
}
@Override public String valueAsString(Fieldable field) {
return dateTimeFormatter.formatter().print(value(field));
return dateTimeFormatter.printer().print(value(field));
}
@Override public String indexedValue(String value) {
return NumericUtils.longToPrefixCoded(dateTimeFormatter.formatter().parseMillis(value));
return NumericUtils.longToPrefixCoded(dateTimeFormatter.parser().parseMillis(value));
}
@Override public String indexedValue(Long value) {
@ -115,15 +115,15 @@ public class JsonDateFieldMapper extends JsonNumberFieldMapper<Long> {
@Override public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
lowerTerm == null ? null : dateTimeFormatter.formatter().parseMillis(lowerTerm),
upperTerm == null ? null : dateTimeFormatter.formatter().parseMillis(upperTerm),
lowerTerm == null ? null : dateTimeFormatter.parser().parseMillis(lowerTerm),
upperTerm == null ? null : dateTimeFormatter.parser().parseMillis(upperTerm),
includeLower, includeUpper);
}
@Override public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
return NumericRangeFilter.newLongRange(names.indexName(), precisionStep,
lowerTerm == null ? null : dateTimeFormatter.formatter().parseMillis(lowerTerm),
upperTerm == null ? null : dateTimeFormatter.formatter().parseMillis(upperTerm),
lowerTerm == null ? null : dateTimeFormatter.parser().parseMillis(lowerTerm),
upperTerm == null ? null : dateTimeFormatter.parser().parseMillis(upperTerm),
includeLower, includeUpper);
}
@ -137,7 +137,7 @@ public class JsonDateFieldMapper extends JsonNumberFieldMapper<Long> {
if (dateAsString == null) {
return null;
}
long value = dateTimeFormatter.formatter().parseMillis(dateAsString);
long value = dateTimeFormatter.parser().parseMillis(dateAsString);
Field field = null;
if (stored()) {
field = new Field(names.indexName(), Numbers.longToBytes(value), store);

View File

@ -310,7 +310,7 @@ public class JsonObjectMapper implements JsonMapper {
boolean isDate = false;
for (FormatDateTimeFormatter dateTimeFormatter : dateTimeFormatters) {
try {
dateTimeFormatter.formatter().parseMillis(jsonContext.jp().getText());
dateTimeFormatter.parser().parseMillis(jsonContext.jp().getText());
mapper = dateField(currentFieldName).dateTimeFormatter(dateTimeFormatter).build(builderContext);
isDate = true;
break;

View File

@ -51,7 +51,7 @@ public class RestMainAction extends BaseRestHandler {
JsonNode rootNode;
int quotesSize;
try {
rootNode = Jackson.newObjectMapper().readValue(Classes.getDefaultClassLoader().getResourceAsStream("org/elasticsearch/rest/action/main/quotes.json"), JsonNode.class);
rootNode = Jackson.defaultObjectMapper().readValue(Classes.getDefaultClassLoader().getResourceAsStream("org/elasticsearch/rest/action/main/quotes.json"), JsonNode.class);
ArrayNode arrayNode = (ArrayNode) rootNode.get("quotes");
quotesSize = Iterators.size(arrayNode.getElements());
} catch (Exception e) {

View File

@ -33,18 +33,29 @@ public class FormatDateTimeFormatter {
private final String format;
private final DateTimeFormatter formatter;
private final DateTimeFormatter parser;
public FormatDateTimeFormatter(String format, DateTimeFormatter formatter) {
private final DateTimeFormatter printer;
public FormatDateTimeFormatter(String format, DateTimeFormatter parser) {
this(format, parser, parser);
}
public FormatDateTimeFormatter(String format, DateTimeFormatter parser, DateTimeFormatter printer) {
this.format = format;
this.formatter = formatter;
this.parser = parser;
this.printer = printer;
}
public String format() {
return format;
}
public DateTimeFormatter formatter() {
return formatter;
public DateTimeFormatter parser() {
return parser;
}
public DateTimeFormatter printer() {
return this.printer;
}
}

View File

@ -73,7 +73,10 @@ public class Joda {
} else if ("dateHourMinuteSecondMillis".equals(input)) {
formatter = ISODateTimeFormat.dateHourMinuteSecondMillis();
} else if ("dateOptionalTime".equals(input)) {
formatter = ISODateTimeFormat.dateOptionalTimeParser();
// in this case, we have a separate parser and printer since the dataOptionalTimeParser can't print
return new FormatDateTimeFormatter(input,
ISODateTimeFormat.dateOptionalTimeParser().withZone(DateTimeZone.UTC),
ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC));
} else if ("dateTime".equals(input)) {
formatter = ISODateTimeFormat.dateTime();
} else if ("dateTimeNoMillis".equals(input)) {

View File

@ -19,10 +19,21 @@
package org.elasticsearch.util.json;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.deser.CustomDeserializerFactory;
import org.codehaus.jackson.map.deser.StdDeserializer;
import org.codehaus.jackson.map.deser.StdDeserializerProvider;
import org.codehaus.jackson.map.ser.CustomSerializerFactory;
import org.codehaus.jackson.map.ser.SerializerBase;
import org.elasticsearch.util.joda.FormatDateTimeFormatter;
import org.elasticsearch.util.joda.Joda;
import org.joda.time.DateTime;
import java.io.IOException;
import java.util.Date;
/**
* A set of helper methods for Jackson.
@ -33,14 +44,21 @@ public final class Jackson {
private static final JsonFactory defaultJsonFactory;
private static final ObjectMapper defaultObjectMapper;
static {
defaultJsonFactory = newJsonFactory();
defaultObjectMapper = newObjectMapper();
}
public static JsonFactory defaultJsonFactory() {
return defaultJsonFactory;
}
public static ObjectMapper defaultObjectMapper() {
return defaultObjectMapper;
}
public static JsonFactory newJsonFactory() {
JsonFactory jsonFactory = new JsonFactory();
jsonFactory.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
@ -52,10 +70,82 @@ public final class Jackson {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true);
CustomSerializerFactory serializerFactory = new CustomSerializerFactory();
serializerFactory.addSpecificMapping(Date.class, new DateSerializer());
serializerFactory.addSpecificMapping(DateTime.class, new DateTimeSerializer());
mapper.setSerializerFactory(serializerFactory);
CustomDeserializerFactory deserializerFactory = new CustomDeserializerFactory();
deserializerFactory.addSpecificMapping(Date.class, new DateDeserializer());
deserializerFactory.addSpecificMapping(DateTime.class, new DateTimeDeserializer());
mapper.setDeserializerProvider(new StdDeserializerProvider(deserializerFactory));
return mapper;
}
private Jackson() {
}
public static class DateDeserializer extends StdDeserializer<Date> {
private final FormatDateTimeFormatter formatter = Joda.forPattern("dateTime");
public DateDeserializer() {
super(Date.class);
}
@Override public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonToken t = jp.getCurrentToken();
if (t == JsonToken.VALUE_STRING) {
return new Date(formatter.parser().parseMillis(jp.getText()));
}
throw ctxt.mappingException(getValueClass());
}
}
public final static class DateSerializer extends SerializerBase<Date> {
private final FormatDateTimeFormatter formatter = Joda.forPattern("dateTime");
@Override public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
jgen.writeString(formatter.parser().print(value.getTime()));
}
@Override public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
return createSchemaNode("string", true);
}
}
public static class DateTimeDeserializer extends StdDeserializer<DateTime> {
private final FormatDateTimeFormatter formatter = Joda.forPattern("dateTime");
public DateTimeDeserializer() {
super(DateTime.class);
}
@Override public DateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonToken t = jp.getCurrentToken();
if (t == JsonToken.VALUE_STRING) {
return formatter.parser().parseDateTime(jp.getText());
}
throw ctxt.mappingException(getValueClass());
}
}
public final static class DateTimeSerializer extends SerializerBase<DateTime> {
private final FormatDateTimeFormatter formatter = Joda.forPattern("dateTime");
@Override public void serialize(DateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
jgen.writeString(formatter.printer().print(value));
}
@Override public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
return createSchemaNode("string", true);
}
}
}

View File

@ -22,17 +22,24 @@ package org.elasticsearch.util.json;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.elasticsearch.util.MapBuilder;
import org.elasticsearch.util.io.FastByteArrayInputStream;
import org.elasticsearch.util.io.FastByteArrayOutputStream;
import org.elasticsearch.util.io.FastCharArrayWriter;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
import java.util.Date;
import java.util.Map;
import static org.elasticsearch.util.json.Jackson.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
/**
* @author kimchy (Shay Banon)
* @author kimchy (shay.banon)
*/
@Test
public class JsonBuilderTests {
@Test public void verifyReuseJsonGenerator() throws Exception {
@ -83,4 +90,14 @@ public class JsonBuilderTests {
JsonNode node = Jackson.newObjectMapper().readValue(new FastByteArrayInputStream(data), JsonNode.class);
assertThat(node.get("source").get("test").getTextValue(), equalTo("value"));
}
@Test public void testDatesObjectMapper() throws Exception {
Date date = new Date();
DateTime dateTime = new DateTime();
Map<String, Object> data = MapBuilder.<String, Object>newMapBuilder()
.put("date", date)
.put("dateTime", dateTime)
.map();
System.out.println("Data: " + defaultObjectMapper().writeValueAsString(data));
}
}