Wire Format

The wire format describes the way entities are serialized to JSON.

Here is some sample code that dumps the entire iModel to JSON.

/**
 * This example code shows one possible way to dump an iModel to JSON.
 * The strategy employed by this example is as follows:
 * - Use the GUID of the iModel to create a new directory
 * - Create a JSON file per Model under that directory
 * - Output a JSON string and newline character per Element into the Model JSON file
 *
 * > Note: The Model JSON file is formatted in such a way that it can either be loaded as JSON or optionally read line-by-line to conserve memory.
 */
class DumpIModel {
  public static dump(iModel: IModelDb, baseDir: string): void {
    // Use the GUID of the iModel to create a new directory
    const outputDir = path.join(baseDir, iModel.getGuid().toString());
    if (!fs.existsSync(outputDir)) {
      fs.mkdirSync(outputDir);
    }
    // Iterate each Model
    const sql = `SELECT ECInstanceId AS id FROM ${Model.classFullName}`;
    iModel.withPreparedStatement(sql, (statement: ECSqlStatement) => {
      while (DbResult.BE_SQLITE_ROW === statement.step()) {
        const row = statement.getRow();
        DumpIModel.dumpModel(iModel, row.id, outputDir);
      }
    });
  }

  private static dumpModel(iModel: IModelDb, modelId: Id64String, outputDir: string): void {
    // Use the Id of the Model to create a JSON output file
    const outputFile = path.join(outputDir, `${modelId.toString()}.json`);
    fs.writeFileSync(outputFile, "[");
    // ECSQL to SELECT every Element in the specified Model
    const sql = `SELECT ECInstanceId AS id FROM ${Element.classFullName} WHERE Model.Id=:modelId`;
    iModel.withPreparedStatement(sql, (statement: ECSqlStatement) => {
      statement.bindId("modelId", modelId);
      let isFirstEntry = true;
      while (DbResult.BE_SQLITE_ROW === statement.step()) {
        isFirstEntry ? fs.appendFileSync(outputFile, "\n") : fs.appendFileSync(outputFile, ",\n");
        isFirstEntry = false;
        const row = statement.getRow();
        // Get the ElementProps (including the geometry detail) for the specified Element
        const elementProps = iModel.elements.getElementProps({ id: row.id, wantGeometry: true });
        // Output the ElementProps as a JSON string
        fs.appendFileSync(outputFile, JSON.stringify(elementProps));
      }
    });
    fs.appendFileSync(outputFile, "\n]");
  }
}

Below are examples of wire formats for a few interesting iModel.js classes.

Root Subject

The following code:

    const elementProps = iModel.elements.getElementProps(IModel.rootSubjectId) as ElementProps;
    const json = JSON.stringify(elementProps, undefined, 2);

Produces the following result:

    {
      "classFullName": "BisCore:Subject",
      "code": {
        "scope": "0x1",
        "spec": "0x1f",
        "value": "DgnDbTestUtils"
      },
      "description": "",
      "id": "0x1",
      "model": "0x1"
    }

RepositoryModel

The following code:

    const modelProps = iModel.models.getModel(IModel.repositoryModelId) as ModelProps;
    const json = JSON.stringify(modelProps, undefined, 2);

Produces the following result:

    {
      "classFullName": "BisCore:RepositoryModel",
      "id": "0x1",
      "isPrivate": false,
      "isTemplate": false,
      "jsonProperties": {},
      "modeledElement": {
        "id": "0x1",
        "relClassName": "BisCore:ModelModelsElement",
      },
      "parentModel": "0x1",
      "name": "DgnDbTestUtils"
    }

GeometricElement3d

The following code produces JSON at various stages on the way to creating a GeometricElement3d:

    // Construct an Arc3d (local coordinates)
    const center = new Point3d(0, 0, 0);
    const radius = 1;
    const sweep = AngleSweep.createStartEnd(Angle.createDegrees(90), Angle.createDegrees(180));
    const arc = Arc3d.createXY(center, radius, sweep);
    const arcJson = JSON.stringify(arc, undefined, 2);

    // Construct a LineString3d (local coordinates)
    const points: Point3d[] = [
      new Point3d(0, 0, 0),
      new Point3d(1, 2, 0),
      new Point3d(1, 2, 4),
    ];
    const lineString = LineString3d.createPoints(points);
    const lineStringJson = JSON.stringify(lineString, undefined, 2);

    // Construct a GeometryStream containing the Arc3d and LineString3d created above (local coordinates)
    const builder = new GeometryStreamBuilder();
    builder.appendGeometry(arc);
    builder.appendGeometry(lineString);
    const geometryStreamJson: string = JSON.stringify(builder.geometryStream, undefined, 2);

    // Construct a Placement (world coordinates)
    const origin = new Point3d(100, 100, 0);
    const angles = YawPitchRollAngles.createDegrees(0, 90, 0);
    const placement: Placement3dProps = { origin, angles };
    const placementJson = JSON.stringify(placement, undefined, 2);

    // Construct a GeometricElement3d using the GeometryStream and Placement created above
    const elementProps: GeometricElement3dProps = {
      classFullName: "Generic:PhysicalObject",
      model: modelId,
      code: Code.createEmpty(),
      category: categoryId,
      placement,
      geom: builder.geometryStream,
    };
    const elementPropsJson = JSON.stringify(elementProps, undefined, 2);

Arc3d JSON

Below is the JSON from the Arc3d:

    {
      "center": [
        0,
        0,
        0
      ],
      "sweep": [
        90,
        180
      ],
      "vector0": [
        1,
        0,
        0
      ],
      "vector90": [
        0,
        1,
        0
      ]
    }

LineString3d JSON

Below is the JSON from the LineString3d:


      [
        [
          0,
          0,
          0
        ],
        [
          1,
          2,
          0
        ],
        [
          1,
          2,
          4
        ]
      ]

GeometryStream JSON

Below is the JSON from the GeometryStream that contains the Arc3d and LineString3d that were previously created. This shows that a GeometryStream is just an array of entries:

      [
        {
          "arc": {
            "center": [
              0,
              0,
              0
            ],
            "vectorX": [
              1,
              0,
              0
            ],
            "vectorY": [
              0,
              1,
              0
            ],
            "sweepStartEnd": [
              90,
              180
            ]
          }
        },
        {
          "lineString": [
            [
              0,
              0,
              0
            ],
            [
              1,
              2,
              0
            ],
            [
              1,
              2,
              4
            ]
          ]
        }
      ]

Placement3d JSON

Below is the JSON from the Placement3d that will be used to place the GeometryStream in world coordinates:

    {
      "origin": [
        100,
        100,
        0
      ],
      "angles": {
        "pitch": 90
      }
    }

GeometricElement3d JSON

Below is the JSON from the GeometricElement3d. This shows that the JSON from the GeometricElement3d contains the JSON from the objects used to create it:

    {
      "classFullName": "Generic:PhysicalObject",
      "model": "0",
      "code": {
        "spec": "0x1",
        "scope": "0x1",
        "value": ""
      },
      "category": "0",
      "placement": {
        "origin": [
          100,
          100,
          0
        ],
        "angles": {
          "pitch": 90
        }
      },
      "geom": [
        {
          "arc": {
            "center": [
              0,
              0,
              0
            ],
            "vectorX": [
              1,
              0,
              0
            ],
            "vectorY": [
              0,
              1,
              0
            ],
            "sweepStartEnd": [
              90,
              180
            ]
          }
        },
        {
          "lineString": [
            [
              0,
              0,
              0
            ],
            [
              1,
              2,
              0
            ],
            [
              1,
              2,
              4
            ]
          ]
        }
      ]
    }

Last Updated: 20 September, 2019