static ManifestFile fromJson(JsonNode jsonNode) {
    Preconditions.checkArgument(jsonNode != null, "Invalid JSON node for manifest file: null");
    Preconditions.checkArgument(
        jsonNode.isObject(), "Invalid JSON node for manifest file: non-object (%s)", jsonNode);

    String path = JsonUtil.getString(PATH, jsonNode);
    long length = JsonUtil.getLong(LENGTH, jsonNode);
    int specId = JsonUtil.getInt(SPEC_ID, jsonNode);

    ManifestContent manifestContent = null;
    if (jsonNode.has(CONTENT)) {
      manifestContent = ManifestContent.fromId(JsonUtil.getInt(CONTENT, jsonNode));
    }

    long sequenceNumber = JsonUtil.getLong(SEQUENCE_NUMBER, jsonNode);
    long minSequenceNumber = JsonUtil.getLong(MIN_SEQUENCE_NUMBER, jsonNode);

    Long addedSnapshotId = null;
    if (jsonNode.has(ADDED_SNAPSHOT_ID)) {
      addedSnapshotId = JsonUtil.getLong(ADDED_SNAPSHOT_ID, jsonNode);
    }

    Integer addedFilesCount = null;
    if (jsonNode.has(ADDED_FILES_COUNT)) {
      addedFilesCount = JsonUtil.getInt(ADDED_FILES_COUNT, jsonNode);
    }

    Integer existingFilesCount = null;
    if (jsonNode.has(EXISTING_FILES_COUNT)) {
      existingFilesCount = JsonUtil.getInt(EXISTING_FILES_COUNT, jsonNode);
    }

    Integer deletedFilesCount = null;
    if (jsonNode.has(DELETED_FILES_COUNT)) {
      deletedFilesCount = JsonUtil.getInt(DELETED_FILES_COUNT, jsonNode);
    }

    Long addedRowsCount = null;
    if (jsonNode.has(ADDED_ROWS_COUNT)) {
      addedRowsCount = JsonUtil.getLong(ADDED_ROWS_COUNT, jsonNode);
    }

    Long existingRowsCount = null;
    if (jsonNode.has(EXISTING_ROWS_COUNT)) {
      existingRowsCount = JsonUtil.getLong(EXISTING_ROWS_COUNT, jsonNode);
    }

    Long deletedRowsCount = null;
    if (jsonNode.has(DELETED_ROWS_COUNT)) {
      deletedRowsCount = JsonUtil.getLong(DELETED_ROWS_COUNT, jsonNode);
    }

    List<ManifestFile.PartitionFieldSummary> partitionFieldSummaries = null;
    if (jsonNode.has(PARTITION_FIELD_SUMMARY)) {
      JsonNode summaryArray = JsonUtil.get(PARTITION_FIELD_SUMMARY, jsonNode);
      Preconditions.checkArgument(
          summaryArray.isArray(),
          "Invalid JSON node for partition field summaries: non-array (%s)",
          summaryArray);

      ImmutableList.Builder<ManifestFile.PartitionFieldSummary> builder = ImmutableList.builder();
      for (JsonNode summaryNode : summaryArray) {
        ManifestFile.PartitionFieldSummary summary =
            PartitionFieldSummaryParser.fromJson(summaryNode);
        builder.add(summary);
      }

      partitionFieldSummaries = builder.build();
    }

    ByteBuffer keyMetadata = JsonUtil.getByteBufferOrNull(KEY_METADATA, jsonNode);

    return new GenericManifestFile(
        path,
        length,
        specId,
        manifestContent,
        sequenceNumber,
        minSequenceNumber,
        addedSnapshotId,
        partitionFieldSummaries,
        keyMetadata,
        addedFilesCount,
        addedRowsCount,
        existingFilesCount,
        existingRowsCount,
        deletedFilesCount,
        deletedRowsCount);
  }
@Test
  public void invalidJsonNode() throws Exception {
    String jsonStr = "{\"str\":\"1\", \"arr\":[]}";
    ObjectMapper mapper = new ObjectMapper();
    JsonNode rootNode = mapper.reader().readTree(jsonStr);

    assertThatThrownBy(() -> ManifestFileParser.fromJson(rootNode.get("str")))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessageContaining("Invalid JSON node for manifest file: non-object ");

    assertThatThrownBy(() -> ManifestFileParser.fromJson(rootNode.get("arr")))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessageContaining("Invalid JSON node for manifest file: non-object ");
  }