Skip to content

Export and Import NBO-XML

NBO-XML is the exchange format of the model. Any element of the tree can be written to XML and read back, so serialization starts from whatever you hold: a whole OME document, one Instrument, or a single Objective.

Overview

Method Description
toXmlString() Returns the element and its content as an XML string
toXmlFile(path) Writes the element and its content to a file
fromXmlString(xml) Replaces the content of the element with that of an XML string
fromXmlFile(path) Replaces the content of the element with that of an XML file

Import replaces rather than merges: whatever the element held is discarded, which makes repeated imports into the same object safe.

All four methods take a trailing validate argument, True by default, covering three things.

Checked With validate=True With validate=False
Restricted values A value violating an enumeration, a pattern, or a range raises InscoperNBOValidationException The value is written or stored as it is
Required attributes An element missing a required attribute raises on export The element is written without it
Schema defaults Attributes carrying a default are written explicitly Only the attributes set are written

Required attributes surprise most often, because the schema asks for more than it seems: twelve on Objective, seven on Detector. A validated export is therefore also a compliance check. Disable validation only to serialize an incomplete draft, or to pass through data from a non-compliant source.

Note

The import example reads instrument_sample.xml. Download it below, or point SAMPLE_PATH at an NBO-XML document of your own.

instrument_sample.xml

Export to XML

BinData shows what validation adds to the output: its Compression attribute defaults to none in the schema, so a validated export writes it and an unvalidated one omits it.

import os
import tempfile

import inscoper_nbo

# Build a small tree: an image carrying three stage positions
image = inscoper_nbo.Image()

image_id = inscoper_nbo.ImageID_Type()
image_id.set("Image:1")
image.setID(image_id)

name = inscoper_nbo.Denomination_Type()
name.set("Field 1")
image.setName(name)

for index, (x, y) in enumerate([(0.0, 0.0), (120.5, 0.0), (241.0, 0.0)]):
    stage_label = inscoper_nbo.StageLabel()
    stage_label.setName(f"Position {index}")
    stage_label.setX(x)
    stage_label.setY(y)
    image.addToStageLabel_List(stage_label)

# Step 1: Serialize to a string
print(image.toXmlString())

# Step 2: Serialize to a file
output_path = os.path.join(tempfile.gettempdir(), "image.xml")
image.toXmlFile(output_path)
print(f"Written to {output_path}")

# Step 3: Control validation on export
# Export validates the document by default: an element missing a required
# attribute raises InscoperNBOValidationException, and attributes that carry a
# schema default are written explicitly. Pass False to write exactly what was
# set, at the cost of producing a document that may not be schema-valid.
bin_data = inscoper_nbo.BinData()
bin_data.setBigEndian(True)
length = inscoper_nbo.NonNegativeLong_Type()
length.set(12)
bin_data.setLength(length)

print(bin_data.toXmlString())        # validated: Compression="none" is written
print(bin_data.toXmlString(False))   # as set: Compression is omitted

#include <NBO/NBO/BinData.h>
#include <NBO/NBO/Denomination_Type.h>
#include <NBO/NBO/Image.h>
#include <NBO/NBO/ImageID_Type.h>
#include <NBO/NBO/NonNegativeLong_Type.h>
#include <NBO/NBO/StageLabel.h>

#include <filesystem>
#include <iostream>
#include <memory>

using namespace Inscoper;

int main() {
    // Build a small tree: an image carrying three stage positions
    NBO::Image image;

    NBO::ImageID_TypePtr imageId = std::make_shared<NBO::ImageID_Type>();
    imageId->set("Image:1");
    image.setID(imageId);

    NBO::Denomination_TypePtr name = std::make_shared<NBO::Denomination_Type>();
    name->set("Field 1");
    image.setName(name);

    const float positions[3][2] = {{0.0f, 0.0f}, {120.5f, 0.0f}, {241.0f, 0.0f}};
    for (int index = 0; index < 3; ++index) {
        NBO::StageLabelPtr stageLabel = std::make_shared<NBO::StageLabel>();
        stageLabel->setName("Position " + std::to_string(index));
        stageLabel->setX(positions[index][0]);
        stageLabel->setY(positions[index][1]);
        image.addToStageLabel_List(stageLabel);
    }

    // Step 1: Serialize to a string
    std::cout << image.toXmlString() << std::endl;

    // Step 2: Serialize to a file
    std::filesystem::path outputPath = std::filesystem::temp_directory_path() / "image.xml";
    image.toXmlFile(outputPath.string());
    std::cout << "Written to " << outputPath.string() << std::endl;

    // Step 3: Control validation on export
    // Export validates the document by default: an element missing a required
    // attribute throws InscoperNBOValidationException, and attributes that carry a
    // schema default are written explicitly. Pass false to write exactly what was
    // set, at the cost of producing a document that may not be schema-valid.
    NBO::BinData binData;
    binData.setBigEndian(true);
    NBO::NonNegativeLong_TypePtr length = std::make_shared<NBO::NonNegativeLong_Type>();
    length->set(12);
    binData.setLength(length);

    std::cout << binData.toXmlString() << std::endl;       // validated: Compression="none"
    std::cout << binData.toXmlString(false) << std::endl;  // as set: Compression is omitted

    return 0;
}

import com.inscoper.nbo.BinData;
import com.inscoper.nbo.Denomination_Type;
import com.inscoper.nbo.Image;
import com.inscoper.nbo.ImageID_Type;
import com.inscoper.nbo.NonNegativeLong_Type;
import com.inscoper.nbo.StageLabel;

import java.nio.file.Path;
import java.nio.file.Paths;

public class ExportXml {
    public static void main(String[] args) {
        // Build a small tree: an image carrying three stage positions
        Image image = new Image();

        ImageID_Type imageId = new ImageID_Type();
        imageId.set("Image:1");
        image.setID(imageId);

        Denomination_Type name = new Denomination_Type();
        name.set("Field 1");
        image.setName(name);

        float[][] positions = {{0.0f, 0.0f}, {120.5f, 0.0f}, {241.0f, 0.0f}};
        for (int index = 0; index < positions.length; index++) {
            StageLabel stageLabel = new StageLabel();
            stageLabel.setName("Position " + index);
            stageLabel.setX(positions[index][0]);
            stageLabel.setY(positions[index][1]);
            image.addToStageLabel_List(stageLabel);
        }

        // Step 1: Serialize to a string
        System.out.println(image.toXmlString());

        // Step 2: Serialize to a file
        Path outputPath = Paths.get(System.getProperty("java.io.tmpdir"), "image.xml");
        image.toXmlFile(outputPath.toString());
        System.out.println("Written to " + outputPath);

        // Step 3: Control validation on export
        // Export validates the document by default: an element missing a required
        // attribute throws InscoperNBOValidationException, and attributes that carry a
        // schema default are written explicitly. Pass false to write exactly what was
        // set, at the cost of producing a document that may not be schema-valid.
        BinData binData = new BinData();
        binData.setBigEndian(true);
        NonNegativeLong_Type length = new NonNegativeLong_Type();
        length.set(12);
        binData.setLength(length);

        System.out.println(binData.toXmlString());        // validated: Compression="none"
        System.out.println(binData.toXmlString(false));   // as set: Compression is omitted
    }
}

Import from XML

Imported documents are checked element by element, so a malformed identifier is reported at import instead of surfacing later in the pipeline.

import inscoper_nbo

SAMPLE_PATH = "instrument_sample.xml"

# Step 1: Import from a string
# Importing replaces the whole content of the target object.
image = inscoper_nbo.Image()

image.fromXmlString(
    """<?xml version="1.0"?>
<Image ID="Image:1" Name="Field 1">
  <StageLabel Name="Position 0" X="0" Y="0" />
  <StageLabel Name="Position 1" X="120.5" Y="0" />
</Image>"""
)

print(image.getID().get(), image.getName().get())
print(len(image.getStageLabel_List()))

# Step 2: Import from a file
instrument = inscoper_nbo.Instrument()
instrument.fromXmlFile(SAMPLE_PATH)

print(instrument.getID().get())
for objective in instrument.getObjective_List():
    print(objective.getModel(), objective.getMagnification())

# Step 3: Imported documents are validated
# Values that violate a schema restriction raise InscoperNBOValidationException,
# which keeps invalid metadata out of the model.
non_compliant = """<?xml version="1.0"?>
<Image ID="NotAnImageIdentifier" />"""

try:
    image.fromXmlString(non_compliant)
except inscoper_nbo.InscoperNBOValidationException as error:
    print(f"Rejected document: {error}")

# Pass False to import a non-compliant document as it is
image.fromXmlString(non_compliant, False)
print(image.getID().get())

#include <NBO/NBO/Image.h>
#include <NBO/NBO/Instrument.h>
#include <NBO/Shared/Exception/InscoperNBOException.h>

#include <iostream>

using namespace Inscoper;

const std::string SAMPLE_PATH = "instrument_sample.xml";

int main() {
    // Step 1: Import from a string
    // Importing replaces the whole content of the target object.
    NBO::Image image;

    image.fromXmlString(R"(<?xml version="1.0"?>
<Image ID="Image:1" Name="Field 1">
  <StageLabel Name="Position 0" X="0" Y="0" />
  <StageLabel Name="Position 1" X="120.5" Y="0" />
</Image>)");

    std::cout << image.getID()->get() << " " << image.getName()->get() << std::endl;
    std::cout << image.getStageLabel_List().size() << std::endl;

    // Step 2: Import from a file
    NBO::Instrument instrument;
    instrument.fromXmlFile(SAMPLE_PATH);

    std::cout << instrument.getID()->get() << std::endl;
    for (const auto &objective : instrument.getObjective_List()) {
        std::cout << objective->getModel() << " " << objective->getMagnification() << std::endl;
    }

    // Step 3: Imported documents are validated
    // Values that violate the schema restrictions throw
    // InscoperNBOValidationException, which keeps invalid metadata out of the model.
    const std::string nonCompliant = R"(<?xml version="1.0"?>
<Image ID="NotAnImageIdentifier" />)";

    try {
        image.fromXmlString(nonCompliant);
    } catch (const NBO::InscoperNBOValidationException &error) {
        std::cout << "Rejected document: " << error.what() << std::endl;
    }

    // Pass false to import a non-compliant document as it is
    image.fromXmlString(nonCompliant, false);
    std::cout << image.getID()->get() << std::endl;

    return 0;
}

import com.inscoper.nbo.Image;
import com.inscoper.nbo.Instrument;
import com.inscoper.nbo.Objective;

public class ImportXml {

    static final String SAMPLE_PATH = "instrument_sample.xml";

    public static void main(String[] args) {
        // Step 1: Import from a string
        // Importing replaces the whole content of the target object.
        Image image = new Image();

        image.fromXmlString("""
                <?xml version="1.0"?>
                <Image ID="Image:1" Name="Field 1">
                  <StageLabel Name="Position 0" X="0" Y="0" />
                  <StageLabel Name="Position 1" X="120.5" Y="0" />
                </Image>""");

        System.out.println(image.getID().get() + " " + image.getName().get());
        System.out.println(image.getStageLabel_List().size());

        // Step 2: Import from a file
        Instrument instrument = new Instrument();
        instrument.fromXmlFile(SAMPLE_PATH);

        System.out.println(instrument.getID().get());
        for (Objective objective : instrument.getObjective_List()) {
            System.out.println(objective.getModel() + " " + objective.getMagnification());
        }

        // Step 3: Imported documents are validated
        // Values that violate the schema restrictions throw
        // InscoperNBOValidationException, which keeps invalid metadata out of the model.
        String nonCompliant = """
                <?xml version="1.0"?>
                <Image ID="NotAnImageIdentifier" />""";

        try {
            image.fromXmlString(nonCompliant);
        } catch (Exception error) {
            System.out.println("Rejected document: " + error.getMessage());
        }

        // Pass false to import a non-compliant document as it is
        image.fromXmlString(nonCompliant, false);
        System.out.println(image.getID().get());
    }
}