json - 严格模式下的MongoDB日期格式

标签 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> 之后带有强制时区字段。

    • MongoDB JSON 解析器目前不支持加载表示 Unix 纪元之前的日期的 ISO-8601 字符串。在格式化前纪元日期和超过系统 time_t 类型可以容纳的日期时,使用以下格式: { "$date": { "$numberLong": "dateAsMilliseconds"} }

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

谢谢。

最佳答案

显然 没有 Java 驱动程序偏离官方规范的充分理由。唯一的异常(exception)是那些不能用 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();
   }
}

关于json - 严格模式下的MongoDB日期格式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38014515/

相关文章:

javascript - $.ajax post 请求头字段Content-Type不允许CORS

java - 如何在 JHipster 中配置多个数据源(Oracle 和 MongoDB)?

mongodb - com.mongodb.MongoSocketOpenException : when connecting to mongodb docker container 异常

java - 以面向对象的方式管理 Java 中的 Mongodb 连接

java - 使用 JSON 将 2 个参数传递给 struts2 操作

c++ - 适用于C++的wso2 Web服务框架中的JSON支持

java - 使用 JQuery 在 Javascript 中解析外部 JSON 文件(包含数组元素)

mongodb - React-Native 与 MongoDB 简单连接

node.js - Mongoosastic - { [错误 : No Living connections] message: 'No Living connections' }

java - 在建立与 MongoDB 数据库的连接的方法中使用 Dropwizard 配置