get jackson object mapper to support dates as elasticsearch expects them
This commit is contained in:
parent
58319b1113
commit
baaac70da5
|
@ -55,7 +55,7 @@ public class JacksonTypesBenchmark {
|
||||||
public JacksonTypesBenchmark(String jsonString) throws IOException {
|
public JacksonTypesBenchmark(String jsonString) throws IOException {
|
||||||
Preconditions.checkNotNull(jsonString, "jsonString must have a value");
|
Preconditions.checkNotNull(jsonString, "jsonString must have a value");
|
||||||
this.jsonString = jsonString;
|
this.jsonString = jsonString;
|
||||||
this.objectMapper = newObjectMapper();
|
this.objectMapper = defaultObjectMapper();
|
||||||
this.factor = 10;
|
this.factor = 10;
|
||||||
this.cycles = 10000;
|
this.cycles = 10000;
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,8 @@ public class JsonDateFieldMapper extends JsonNumberFieldMapper<Long> {
|
||||||
float boost, boolean omitNorms, boolean omitTermFreqAndPositions,
|
float boost, boolean omitNorms, boolean omitTermFreqAndPositions,
|
||||||
String nullValue) {
|
String nullValue) {
|
||||||
super(names, precisionStep, index, store, boost, omitNorms, omitTermFreqAndPositions,
|
super(names, precisionStep, index, store, boost, omitNorms, omitTermFreqAndPositions,
|
||||||
new NamedAnalyzer("_date/" + precisionStep, new NumericDateAnalyzer(precisionStep, dateTimeFormatter.formatter())),
|
new NamedAnalyzer("_date/" + precisionStep, new NumericDateAnalyzer(precisionStep, dateTimeFormatter.parser())),
|
||||||
new NamedAnalyzer("_date/max", new NumericDateAnalyzer(Integer.MAX_VALUE, dateTimeFormatter.formatter())));
|
new NamedAnalyzer("_date/max", new NumericDateAnalyzer(Integer.MAX_VALUE, dateTimeFormatter.parser())));
|
||||||
this.dateTimeFormatter = dateTimeFormatter;
|
this.dateTimeFormatter = dateTimeFormatter;
|
||||||
this.nullValue = nullValue;
|
this.nullValue = nullValue;
|
||||||
}
|
}
|
||||||
|
@ -102,11 +102,11 @@ public class JsonDateFieldMapper extends JsonNumberFieldMapper<Long> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String valueAsString(Fieldable field) {
|
@Override public String valueAsString(Fieldable field) {
|
||||||
return dateTimeFormatter.formatter().print(value(field));
|
return dateTimeFormatter.printer().print(value(field));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String indexedValue(String value) {
|
@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) {
|
@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) {
|
@Override public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
|
||||||
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
|
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
|
||||||
lowerTerm == null ? null : dateTimeFormatter.formatter().parseMillis(lowerTerm),
|
lowerTerm == null ? null : dateTimeFormatter.parser().parseMillis(lowerTerm),
|
||||||
upperTerm == null ? null : dateTimeFormatter.formatter().parseMillis(upperTerm),
|
upperTerm == null ? null : dateTimeFormatter.parser().parseMillis(upperTerm),
|
||||||
includeLower, includeUpper);
|
includeLower, includeUpper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
|
@Override public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
|
||||||
return NumericRangeFilter.newLongRange(names.indexName(), precisionStep,
|
return NumericRangeFilter.newLongRange(names.indexName(), precisionStep,
|
||||||
lowerTerm == null ? null : dateTimeFormatter.formatter().parseMillis(lowerTerm),
|
lowerTerm == null ? null : dateTimeFormatter.parser().parseMillis(lowerTerm),
|
||||||
upperTerm == null ? null : dateTimeFormatter.formatter().parseMillis(upperTerm),
|
upperTerm == null ? null : dateTimeFormatter.parser().parseMillis(upperTerm),
|
||||||
includeLower, includeUpper);
|
includeLower, includeUpper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ public class JsonDateFieldMapper extends JsonNumberFieldMapper<Long> {
|
||||||
if (dateAsString == null) {
|
if (dateAsString == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
long value = dateTimeFormatter.formatter().parseMillis(dateAsString);
|
long value = dateTimeFormatter.parser().parseMillis(dateAsString);
|
||||||
Field field = null;
|
Field field = null;
|
||||||
if (stored()) {
|
if (stored()) {
|
||||||
field = new Field(names.indexName(), Numbers.longToBytes(value), store);
|
field = new Field(names.indexName(), Numbers.longToBytes(value), store);
|
||||||
|
|
|
@ -310,7 +310,7 @@ public class JsonObjectMapper implements JsonMapper {
|
||||||
boolean isDate = false;
|
boolean isDate = false;
|
||||||
for (FormatDateTimeFormatter dateTimeFormatter : dateTimeFormatters) {
|
for (FormatDateTimeFormatter dateTimeFormatter : dateTimeFormatters) {
|
||||||
try {
|
try {
|
||||||
dateTimeFormatter.formatter().parseMillis(jsonContext.jp().getText());
|
dateTimeFormatter.parser().parseMillis(jsonContext.jp().getText());
|
||||||
mapper = dateField(currentFieldName).dateTimeFormatter(dateTimeFormatter).build(builderContext);
|
mapper = dateField(currentFieldName).dateTimeFormatter(dateTimeFormatter).build(builderContext);
|
||||||
isDate = true;
|
isDate = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class RestMainAction extends BaseRestHandler {
|
||||||
JsonNode rootNode;
|
JsonNode rootNode;
|
||||||
int quotesSize;
|
int quotesSize;
|
||||||
try {
|
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");
|
ArrayNode arrayNode = (ArrayNode) rootNode.get("quotes");
|
||||||
quotesSize = Iterators.size(arrayNode.getElements());
|
quotesSize = Iterators.size(arrayNode.getElements());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -33,18 +33,29 @@ public class FormatDateTimeFormatter {
|
||||||
|
|
||||||
private final String format;
|
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.format = format;
|
||||||
this.formatter = formatter;
|
this.parser = parser;
|
||||||
|
this.printer = printer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String format() {
|
public String format() {
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTimeFormatter formatter() {
|
public DateTimeFormatter parser() {
|
||||||
return formatter;
|
return parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTimeFormatter printer() {
|
||||||
|
return this.printer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,10 @@ public class Joda {
|
||||||
} else if ("dateHourMinuteSecondMillis".equals(input)) {
|
} else if ("dateHourMinuteSecondMillis".equals(input)) {
|
||||||
formatter = ISODateTimeFormat.dateHourMinuteSecondMillis();
|
formatter = ISODateTimeFormat.dateHourMinuteSecondMillis();
|
||||||
} else if ("dateOptionalTime".equals(input)) {
|
} 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)) {
|
} else if ("dateTime".equals(input)) {
|
||||||
formatter = ISODateTimeFormat.dateTime();
|
formatter = ISODateTimeFormat.dateTime();
|
||||||
} else if ("dateTimeNoMillis".equals(input)) {
|
} else if ("dateTimeNoMillis".equals(input)) {
|
||||||
|
|
|
@ -19,10 +19,21 @@
|
||||||
|
|
||||||
package org.elasticsearch.util.json;
|
package org.elasticsearch.util.json;
|
||||||
|
|
||||||
import org.codehaus.jackson.JsonFactory;
|
import org.codehaus.jackson.*;
|
||||||
import org.codehaus.jackson.JsonGenerator;
|
import org.codehaus.jackson.map.DeserializationContext;
|
||||||
import org.codehaus.jackson.JsonParser;
|
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
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.
|
* A set of helper methods for Jackson.
|
||||||
|
@ -33,14 +44,21 @@ public final class Jackson {
|
||||||
|
|
||||||
private static final JsonFactory defaultJsonFactory;
|
private static final JsonFactory defaultJsonFactory;
|
||||||
|
|
||||||
|
private static final ObjectMapper defaultObjectMapper;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
defaultJsonFactory = newJsonFactory();
|
defaultJsonFactory = newJsonFactory();
|
||||||
|
defaultObjectMapper = newObjectMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JsonFactory defaultJsonFactory() {
|
public static JsonFactory defaultJsonFactory() {
|
||||||
return defaultJsonFactory;
|
return defaultJsonFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ObjectMapper defaultObjectMapper() {
|
||||||
|
return defaultObjectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
public static JsonFactory newJsonFactory() {
|
public static JsonFactory newJsonFactory() {
|
||||||
JsonFactory jsonFactory = new JsonFactory();
|
JsonFactory jsonFactory = new JsonFactory();
|
||||||
jsonFactory.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
|
jsonFactory.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
|
||||||
|
@ -52,10 +70,82 @@ public final class Jackson {
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
|
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
|
||||||
mapper.configure(JsonGenerator.Feature.QUOTE_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;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Jackson() {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,17 +22,24 @@ package org.elasticsearch.util.json;
|
||||||
import org.codehaus.jackson.JsonEncoding;
|
import org.codehaus.jackson.JsonEncoding;
|
||||||
import org.codehaus.jackson.JsonGenerator;
|
import org.codehaus.jackson.JsonGenerator;
|
||||||
import org.codehaus.jackson.JsonNode;
|
import org.codehaus.jackson.JsonNode;
|
||||||
|
import org.elasticsearch.util.MapBuilder;
|
||||||
import org.elasticsearch.util.io.FastByteArrayInputStream;
|
import org.elasticsearch.util.io.FastByteArrayInputStream;
|
||||||
import org.elasticsearch.util.io.FastByteArrayOutputStream;
|
import org.elasticsearch.util.io.FastByteArrayOutputStream;
|
||||||
import org.elasticsearch.util.io.FastCharArrayWriter;
|
import org.elasticsearch.util.io.FastCharArrayWriter;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
import org.testng.annotations.Test;
|
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.MatcherAssert.*;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author kimchy (Shay Banon)
|
* @author kimchy (shay.banon)
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public class JsonBuilderTests {
|
public class JsonBuilderTests {
|
||||||
|
|
||||||
@Test public void verifyReuseJsonGenerator() throws Exception {
|
@Test public void verifyReuseJsonGenerator() throws Exception {
|
||||||
|
@ -83,4 +90,14 @@ public class JsonBuilderTests {
|
||||||
JsonNode node = Jackson.newObjectMapper().readValue(new FastByteArrayInputStream(data), JsonNode.class);
|
JsonNode node = Jackson.newObjectMapper().readValue(new FastByteArrayInputStream(data), JsonNode.class);
|
||||||
assertThat(node.get("source").get("test").getTextValue(), equalTo("value"));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue