严格模式下的MongoDB日期格式

时间:2016-06-24 13:21:57

标签: json mongodb mongodb-java

  1. 使用MongoDB java驱动程序,在Document上应用toJson()方法将获得此文档的JSON表示,并将JsonMode设置为STRICT。 以下纪元格式用于日期:{" $ date" :" dateAsMilliseconds" }

  2. 使用mongoexport,我们获得ISO-8601格式。

  3. 见官方文件(https://docs.mongodb.com/manual/reference/mongodb-extended-json/):

    • 在严格模式下,日期是ISO-8601日期格式,在模板YYYY-MM-DDTHH后面有一个强制时区字段:mm:ss.mmm< +/- Offset>。

      < / LI>
    • MongoDB JSON解析器目前不支持加载表示Unix纪元之前的日期的ISO-8601字符串。将早期时间日期和日期格式化为系统的time_t类型可以保存的日期和日期时,将使用以下格式: {&#34; $ date&#34; :{&#34; $ numberLong&#34; :&#34; dateAsMilliseconds&#34; }

  4. 如果有人能解释为什么MongoDB java驱动程序,mongoexport工具和官方文档之间没有使用通用格式,我将不胜感激?

    感谢。

1 个答案:

答案 0 :(得分:1)

显然, NO 有很好的理由让Java驱动程序偏离官方规范。唯一的例外是那些不能用ISO8601格式表达的日期(如B.C.日期......)

作为一种解决方法,我扩展了JsonWriter类并提供了两个toJson静态方法作为如何使用它的示例:

package whatever.package.you.like;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

import org.bson.BSONException;
import org.bson.BsonContextType;
import org.bson.BsonDocument;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.EncoderContext;
import org.bson.conversions.Bson;
import org.bson.json.JsonMode;
import org.bson.json.JsonWriter;
import org.bson.json.JsonWriterSettings;

import com.mongodb.MongoClient;

/**
 * A {@link JsonWriter} extension that conforms to the "strict" JSON format
 * specified by MongoDB for data/time values.
 * 
 * The {@link JsonWriter} class provided in the MongoDB Java driver (version
 * 3.2.2) does not conform to official MongoDB specification for strict mode
 * JSON (see https://docs.mongodb.com/manual/reference/mongodb-extended-json/).
 * This is specifically a problem with the date/time values which get filled
 * with a milliseconds value (i.e. {$date: 309249234098}) instead of the ISO8601
 * date/time (i.e. {$date: "2016-07-14T08:44:23.234Z"}) value which the
 * specification calls for. This extension of {@link JsonWriter} conforms to the
 * MongoDb specification in this regard.
 */
public class ConformingJsonWriter extends JsonWriter {
   private final JsonWriterSettings settings;

   private final Writer writer;

   private boolean writingIndentedDateTime = false;

   /**
    * Creates a new instance which uses {@code writer} to write JSON to.
    *
    * @param writer
    *           the writer to write JSON to.
    */
   public ConformingJsonWriter(final Writer writer) {
      this(writer, new JsonWriterSettings());
   }

   /**
    * Creates a new instance which uses {@code writer} to write JSON to and uses
    * the given settings.
    *
    * @param writer
    *           the writer to write JSON to.
    * @param settings
    *           the settings to apply to this writer.
    */
   public ConformingJsonWriter(final Writer writer,
         final JsonWriterSettings settings) {
      super(writer, settings);
      this.writer = writer;
      this.settings = settings;
      setContext(new Context(null, BsonContextType.TOP_LEVEL, ""));
   }

   private void writeIndentation(int skip) throws IOException {
      for (Context context = getContext()
            .getParentContext(); context != null; context = context
                  .getParentContext()) {
         if (skip-- <= 0) {
            writer.write(settings.getIndentCharacters());
         }
      }
   }

   private static String millisToIso8601(long millis) throws IOException {
      SimpleDateFormat dateFormat = new SimpleDateFormat(
            "yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'");
      dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
      return dateFormat.format(new Date(millis));
   }

   @Override
   protected void doWriteDateTime(final long value) {
      if ((settings.getOutputMode() == JsonMode.STRICT)
            && (value >= -59014396800000L && value <= 253399536000000L)) {
         try {
            writeStartDocument();
            if (settings.isIndent()) {
               writingIndentedDateTime = true;
               writer.write(settings.getNewLineCharacters());
               writeIndentation(0);
            } else {
               writer.write(" ");
            }
            writer.write("\"$date\" : ");
            writer.write("\"");
            writer.write(millisToIso8601(value));
            writer.write("\"");
            writeEndDocument();
            writingIndentedDateTime = false;
         } catch (IOException e) {
            throw new BSONException("Wrapping IOException", e);
         }
      } else {
         super.doWriteDateTime(value);
      }
   }

   @Override
   protected void doWriteEndDocument() {
      if (writingIndentedDateTime) {
         try {
            writer.write(settings.getNewLineCharacters());
            writeIndentation(1);
            writer.write("}");
            if (getContext()
                  .getContextType() == BsonContextType.SCOPE_DOCUMENT) {
               setContext(getContext().getParentContext());
               writeEndDocument();
            } else {
               setContext(getContext().getParentContext());
            }
         } catch (IOException e) {
            throw new BSONException("Wrapping IOException", e);
         }
      } else {
         super.doWriteEndDocument();
      }
   }

   /**
    * Take a {@link Bson} instance and convert it to "strict" JSON
    * representation with no indentation (read, "NOT pretty printed").
    * 
    * @param bson
    *           The {@link Bson} instance to convert
    * @return The JSON representation.
    */
   public static String toJson(Bson bson) {
      return toJson(bson, new JsonWriterSettings());
   }

   /**
    * Take a {@link Bson} instance and convert it to JSON representation.
    * 
    * @param bson
    *           The {@link Bson} instance to convert
    * @param writerSettings
    *           {@link JsonWriterSettings} that specify details about how the
    *           JSON output should look.
    * @return The JSON representation.
    */
   public static String toJson(Bson bson,
         final JsonWriterSettings writerSettings) {
      BsonDocumentCodec encoder = new BsonDocumentCodec();
      ConformingJsonWriter writer = new ConformingJsonWriter(new StringWriter(),
            writerSettings);
      encoder.encode(writer,
            bson.toBsonDocument(BsonDocument.class,
                  MongoClient.getDefaultCodecRegistry()),
            EncoderContext.builder().isEncodingCollectibleDocument(true)
                  .build());
      return writer.getWriter().toString();
   }
}