public static TableMetadata fromJson()

in core/src/main/java/org/apache/iceberg/TableMetadataParser.java [339:584]


  public static TableMetadata fromJson(String metadataLocation, JsonNode node) {
    Preconditions.checkArgument(
        node.isObject(), "Cannot parse metadata from a non-object: %s", node);

    int formatVersion = JsonUtil.getInt(FORMAT_VERSION, node);
    Preconditions.checkArgument(
        formatVersion <= TableMetadata.SUPPORTED_TABLE_FORMAT_VERSION,
        "Cannot read unsupported version %s",
        formatVersion);

    String uuid = JsonUtil.getStringOrNull(TABLE_UUID, node);
    String location = JsonUtil.getString(LOCATION, node);
    long lastSequenceNumber;
    if (formatVersion > 1) {
      lastSequenceNumber = JsonUtil.getLong(LAST_SEQUENCE_NUMBER, node);
    } else {
      lastSequenceNumber = TableMetadata.INITIAL_SEQUENCE_NUMBER;
    }
    int lastAssignedColumnId = JsonUtil.getInt(LAST_COLUMN_ID, node);

    List<Schema> schemas;
    int currentSchemaId;
    Schema schema = null;

    JsonNode schemaArray = node.get(SCHEMAS);
    if (schemaArray != null) {
      Preconditions.checkArgument(
          schemaArray.isArray(), "Cannot parse schemas from non-array: %s", schemaArray);
      // current schema ID is required when the schema array is present
      currentSchemaId = JsonUtil.getInt(CURRENT_SCHEMA_ID, node);

      // parse the schema array
      ImmutableList.Builder<Schema> builder = ImmutableList.builder();
      for (JsonNode schemaNode : schemaArray) {
        Schema current = SchemaParser.fromJson(schemaNode);
        if (current.schemaId() == currentSchemaId) {
          schema = current;
        }
        builder.add(current);
      }

      Preconditions.checkArgument(
          schema != null,
          "Cannot find schema with %s=%s from %s",
          CURRENT_SCHEMA_ID,
          currentSchemaId,
          SCHEMAS);

      schemas = builder.build();

    } else {
      Preconditions.checkArgument(
          formatVersion == 1, "%s must exist in format v%s", SCHEMAS, formatVersion);

      schema = SchemaParser.fromJson(JsonUtil.get(SCHEMA, node));
      currentSchemaId = schema.schemaId();
      schemas = ImmutableList.of(schema);
    }

    JsonNode specArray = node.get(PARTITION_SPECS);
    List<PartitionSpec> specs;
    int defaultSpecId;
    if (specArray != null) {
      Preconditions.checkArgument(
          specArray.isArray(), "Cannot parse partition specs from non-array: %s", specArray);
      // default spec ID is required when the spec array is present
      defaultSpecId = JsonUtil.getInt(DEFAULT_SPEC_ID, node);

      // parse the spec array
      ImmutableList.Builder<PartitionSpec> builder = ImmutableList.builder();
      for (JsonNode spec : specArray) {
        UnboundPartitionSpec unboundSpec = PartitionSpecParser.fromJson(spec);
        if (unboundSpec.specId() == defaultSpecId) {
          builder.add(unboundSpec.bind(schema));
        } else {
          builder.add(unboundSpec.bindUnchecked(schema));
        }
      }
      specs = builder.build();

    } else {
      Preconditions.checkArgument(
          formatVersion == 1, "%s must exist in format v%s", PARTITION_SPECS, formatVersion);
      // partition spec is required for older readers, but is always set to the default if the spec
      // array is set. it is only used to default the spec map is missing, indicating that the
      // table metadata was written by an older writer.
      defaultSpecId = TableMetadata.INITIAL_SPEC_ID;
      specs =
          ImmutableList.of(
              PartitionSpecParser.fromJsonFields(
                  schema, TableMetadata.INITIAL_SPEC_ID, JsonUtil.get(PARTITION_SPEC, node)));
    }

    Integer lastAssignedPartitionId = JsonUtil.getIntOrNull(LAST_PARTITION_ID, node);
    if (lastAssignedPartitionId == null) {
      Preconditions.checkArgument(
          formatVersion == 1, "%s must exist in format v%s", LAST_PARTITION_ID, formatVersion);
      lastAssignedPartitionId =
          specs.stream()
              .mapToInt(PartitionSpec::lastAssignedFieldId)
              .max()
              .orElse(PartitionSpec.unpartitioned().lastAssignedFieldId());
    }

    // parse the sort orders
    JsonNode sortOrderArray = node.get(SORT_ORDERS);
    List<SortOrder> sortOrders;
    int defaultSortOrderId;
    if (sortOrderArray != null) {
      defaultSortOrderId = JsonUtil.getInt(DEFAULT_SORT_ORDER_ID, node);
      ImmutableList.Builder<SortOrder> sortOrdersBuilder = ImmutableList.builder();
      for (JsonNode sortOrder : sortOrderArray) {
        sortOrdersBuilder.add(SortOrderParser.fromJson(schema, sortOrder, defaultSortOrderId));
      }
      sortOrders = sortOrdersBuilder.build();
    } else {
      Preconditions.checkArgument(
          formatVersion == 1, "%s must exist in format v%s", SORT_ORDERS, formatVersion);
      SortOrder defaultSortOrder = SortOrder.unsorted();
      sortOrders = ImmutableList.of(defaultSortOrder);
      defaultSortOrderId = defaultSortOrder.orderId();
    }

    Map<String, String> properties;
    if (node.has(PROPERTIES)) {
      // parse properties map
      properties = JsonUtil.getStringMap(PROPERTIES, node);
    } else {
      properties = ImmutableMap.of();
    }

    Long currentSnapshotId = JsonUtil.getLongOrNull(CURRENT_SNAPSHOT_ID, node);
    if (currentSnapshotId == null) {
      // This field is optional, but internally we set this to -1 when not set
      currentSnapshotId = -1L;
    }

    long lastRowId;
    if (formatVersion >= 3) {
      lastRowId = JsonUtil.getLong(NEXT_ROW_ID, node);
    } else {
      lastRowId = TableMetadata.INITIAL_ROW_ID;
    }

    long lastUpdatedMillis = JsonUtil.getLong(LAST_UPDATED_MILLIS, node);

    List<EncryptedKey> keys;
    if (node.has(ENCRYPTION_KEYS)) {
      keys = JsonUtil.getObjectList(ENCRYPTION_KEYS, node, EncryptedKeyParser::fromJson);
    } else {
      keys = List.of();
    }

    Map<String, SnapshotRef> refs;
    if (node.has(REFS)) {
      refs = refsFromJson(node.get(REFS));
    } else if (currentSnapshotId != -1L) {
      // initialize the main branch if there are no refs
      refs =
          ImmutableMap.of(
              SnapshotRef.MAIN_BRANCH, SnapshotRef.branchBuilder(currentSnapshotId).build());
    } else {
      refs = ImmutableMap.of();
    }

    List<Snapshot> snapshots;
    if (node.has(SNAPSHOTS)) {
      JsonNode snapshotArray = JsonUtil.get(SNAPSHOTS, node);
      Preconditions.checkArgument(
          snapshotArray.isArray(), "Cannot parse snapshots from non-array: %s", snapshotArray);

      snapshots = Lists.newArrayListWithExpectedSize(snapshotArray.size());
      Iterator<JsonNode> iterator = snapshotArray.elements();
      while (iterator.hasNext()) {
        snapshots.add(SnapshotParser.fromJson(iterator.next()));
      }
    } else {
      snapshots = ImmutableList.of();
    }

    List<StatisticsFile> statisticsFiles;
    if (node.has(STATISTICS)) {
      statisticsFiles = statisticsFilesFromJson(node.get(STATISTICS));
    } else {
      statisticsFiles = ImmutableList.of();
    }

    List<PartitionStatisticsFile> partitionStatisticsFiles;
    if (node.has(PARTITION_STATISTICS)) {
      partitionStatisticsFiles = partitionStatsFilesFromJson(node.get(PARTITION_STATISTICS));
    } else {
      partitionStatisticsFiles = ImmutableList.of();
    }

    ImmutableList.Builder<HistoryEntry> entries = ImmutableList.builder();
    if (node.has(SNAPSHOT_LOG)) {
      Iterator<JsonNode> logIterator = node.get(SNAPSHOT_LOG).elements();
      while (logIterator.hasNext()) {
        JsonNode entryNode = logIterator.next();
        entries.add(
            new SnapshotLogEntry(
                JsonUtil.getLong(TIMESTAMP_MS, entryNode),
                JsonUtil.getLong(SNAPSHOT_ID, entryNode)));
      }
    }

    ImmutableList.Builder<MetadataLogEntry> metadataEntries = ImmutableList.builder();
    if (node.has(METADATA_LOG)) {
      Iterator<JsonNode> logIterator = node.get(METADATA_LOG).elements();
      while (logIterator.hasNext()) {
        JsonNode entryNode = logIterator.next();
        metadataEntries.add(
            new MetadataLogEntry(
                JsonUtil.getLong(TIMESTAMP_MS, entryNode),
                JsonUtil.getString(METADATA_FILE, entryNode)));
      }
    }

    return new TableMetadata(
        metadataLocation,
        formatVersion,
        uuid,
        location,
        lastSequenceNumber,
        lastUpdatedMillis,
        lastAssignedColumnId,
        currentSchemaId,
        schemas,
        defaultSpecId,
        specs,
        lastAssignedPartitionId,
        defaultSortOrderId,
        sortOrders,
        properties,
        currentSnapshotId,
        snapshots,
        null,
        entries.build(),
        metadataEntries.build(),
        refs,
        statisticsFiles,
        partitionStatisticsFiles,
        lastRowId,
        keys,
        ImmutableList.of() /* no changes from the file */);
  }