Skip to content

Choice and Inline Elements

Two XSD constructs produce class names that appear nowhere in the schema documentation: <choice> groups and elements declared inline inside their parent. This page shows how to recognize them and how to fill them.

Overview

Construct Generated class Filled through
<choice> group <Parent>_Choice add<Alternative>() or set<Alternative>(), then set<parent>_choice() on the parent
Inline element <Parent>_Inline<Element> Its own accessors, like any other child class

Choice groups

A choice group holds one of several alternatives. The generated method names follow the multiplicity declared in the schema.

Method Meaning
contains<Alternative>() Whether a repeated alternative is present
add<Alternative>(vector) Adds a vector of elements for a repeated alternative
is<Alternative>() Whether an exclusive alternative is the one selected
set<Alternative>(element) Selects an exclusive alternative, clearing the previous one

Pixels shows the repeated form: its choice carries BinData, TiffData, NGFFData, and MetadataOnly children together. OME shows the exclusive form: a document is either a BinaryOnly reference to metadata stored elsewhere, or a full metadata document, never both.

In C++ the same construct is a std::variant. The Python binding exposes the methods above, so no visitor is needed.

Inline elements

Where the schema declares an element inside its parent instead of referencing a named type, the generator scopes a class to that parent. Screen declares Description and PlateRef inline, producing Screen_InlineDescription and Screen_InlinePlateRef. Only the class name differs: accessors, validation, and serialization behave as for named types, and the document carries the plain element name.

Note

Inline classes are not interchangeable between parents. Screen_InlinePlateRef describes the PlateRef of Screen alone, even though other parents declare an element of the same name.

Populate a choice group

The Pixels element below is exported without validation, because only its choice is populated. A validated export would ask for the ten attributes the schema requires on Pixels; Describe an Acquired Image sets them all.

import inscoper_nbo

# Step 1: A choice of repeated alternatives
# Pixels may carry BinData, TiffData, NGFFData, or MetadataOnly children. The
# choice object is filled first, then assigned to its parent.
pixels_choice = inscoper_nbo.Pixels_Choice()
print(pixels_choice.containsBinData(), pixels_choice.containsTiffData())

bin_data_list = inscoper_nbo.BinDataVector()
for compression in ("zlib", "bzip2"):
    bin_data = inscoper_nbo.BinData()
    bin_data.setCompression(compression)
    bin_data.setBigEndian(True)
    length = inscoper_nbo.NonNegativeLong_Type()
    length.set(12)
    bin_data.setLength(length)
    bin_data_list.append(bin_data)

pixels_choice.addBinData(bin_data_list)
print(pixels_choice.containsBinData(), pixels_choice.containsTiffData())

pixels = inscoper_nbo.Pixels()
pixels.setpixels_choice(pixels_choice)

# Pixels declares required attributes of its own, which a validated export would
# demand. Only the choice matters here, so export without validation; see the
# Describe an Acquired Image example for a complete, valid Pixels element.
print(pixels.toXmlString(False))

# Step 2: A choice of exclusive alternatives
# OME holds either a BinaryOnly reference or a full metadata document. Setting
# one alternative clears the other.
ome_choice = inscoper_nbo.OME_Choice()
print(ome_choice.isBinaryOnly(), ome_choice.isOME_Choice_1())

binary_only = inscoper_nbo.OME_Choice_InlineBinaryOnly()
uuid = inscoper_nbo.UniversallyUniqueIdentifier_Type()
uuid.set("urn:uuid:3e450fae-b8f2-4d35-aa54-702168b2487f")
binary_only.setUUID(uuid)
binary_only.setMetadataFile("metadata.ome.xml")
ome_choice.setBinaryOnly(binary_only)
print(ome_choice.isBinaryOnly(), ome_choice.isOME_Choice_1())

full_document = inscoper_nbo.OME_Choice_InlineOME_Choice_1()
project = inscoper_nbo.Project()
project_id = inscoper_nbo.ProjectID_Type()
project_id.set("Project:1")
project.setID(project_id)
project.setName("Mitosis screen")
full_document.addToProject_List(project)
ome_choice.setOME_Choice_1(full_document)
print(ome_choice.isBinaryOnly(), ome_choice.isOME_Choice_1())

ome = inscoper_nbo.OME()
ome.setome_choice(ome_choice)
print(ome.toXmlString())

#include <NBO/NBO/BinData.h>
#include <NBO/NBO/NonNegativeLong_Type.h>
#include <NBO/NBO/OME.h>
#include <NBO/NBO/Pixels.h>
#include <NBO/NBO/Project.h>
#include <NBO/NBO/ProjectID_Type.h>
#include <NBO/NBO/UniversallyUniqueIdentifier_Type.h>

#include <iostream>
#include <memory>
#include <vector>

using namespace Inscoper;

int main() {
    std::cout << std::boolalpha;

    // Step 1: A choice of repeated alternatives
    // Pixels may carry BinData, TiffData, NGFFData, or MetadataOnly children. The
    // choice object is filled first, then assigned to its parent.
    NBO::Pixels_ChoicePtr pixelsChoice = std::make_shared<NBO::Pixels_Choice>();
    std::cout << pixelsChoice->containsBinData() << " " << pixelsChoice->containsTiffData()
              << std::endl;

    std::vector<NBO::BinDataPtr> binDataList;
    for (const std::string &compression : {"zlib", "bzip2"}) {
        NBO::BinDataPtr binData = std::make_shared<NBO::BinData>();
        binData->setCompression(compression);
        binData->setBigEndian(true);
        NBO::NonNegativeLong_TypePtr length = std::make_shared<NBO::NonNegativeLong_Type>();
        length->set(12);
        binData->setLength(length);
        binDataList.push_back(binData);
    }

    pixelsChoice->addBinData(binDataList);
    std::cout << pixelsChoice->containsBinData() << " " << pixelsChoice->containsTiffData()
              << std::endl;

    NBO::Pixels pixels;
    pixels.setpixels_choice(pixelsChoice);

    // Pixels declares required attributes of its own, which a validated export would
    // demand. Only the choice matters here, so export without validation; see the
    // Describe an Acquired Image example for a complete, valid Pixels element.
    std::cout << pixels.toXmlString(false) << std::endl;

    // Step 2: A choice of exclusive alternatives
    // OME holds either a BinaryOnly reference or a full metadata document. Setting
    // one alternative clears the other. In C++ the choice is a std::variant, and the
    // generated is / set helpers replace the need for a visitor.
    NBO::OME_ChoicePtr omeChoice = std::make_shared<NBO::OME_Choice>();
    std::cout << omeChoice->isBinaryOnly() << " " << omeChoice->isOME_Choice_1() << std::endl;

    NBO::OME_Choice::InlineBinaryOnlyPtr binaryOnly =
        std::make_shared<NBO::OME_Choice::InlineBinaryOnly>();
    NBO::UniversallyUniqueIdentifier_TypePtr uuid =
        std::make_shared<NBO::UniversallyUniqueIdentifier_Type>();
    uuid->set("urn:uuid:3e450fae-b8f2-4d35-aa54-702168b2487f");
    binaryOnly->setUUID(uuid);
    binaryOnly->setMetadataFile("metadata.ome.xml");
    omeChoice->setBinaryOnly(binaryOnly);
    std::cout << omeChoice->isBinaryOnly() << " " << omeChoice->isOME_Choice_1() << std::endl;

    NBO::OME_Choice::InlineOME_Choice_1Ptr fullDocument =
        std::make_shared<NBO::OME_Choice::InlineOME_Choice_1>();
    NBO::ProjectPtr project = std::make_shared<NBO::Project>();
    NBO::ProjectID_TypePtr projectId = std::make_shared<NBO::ProjectID_Type>();
    projectId->set("Project:1");
    project->setID(projectId);
    project->setName("Mitosis screen");
    fullDocument->addToProject_List(project);
    omeChoice->setOME_Choice_1(fullDocument);
    std::cout << omeChoice->isBinaryOnly() << " " << omeChoice->isOME_Choice_1() << std::endl;

    NBO::OME ome;
    ome.setome_choice(omeChoice);
    std::cout << ome.toXmlString() << std::endl;

    return 0;
}

import com.inscoper.nbo.BinData;
import com.inscoper.nbo.BinDataVector;
import com.inscoper.nbo.NonNegativeLong_Type;
import com.inscoper.nbo.OME;
import com.inscoper.nbo.OME_Choice;
import com.inscoper.nbo.OME_Choice_InlineBinaryOnly;
import com.inscoper.nbo.OME_Choice_InlineOME_Choice_1;
import com.inscoper.nbo.Pixels;
import com.inscoper.nbo.Pixels_Choice;
import com.inscoper.nbo.Project;
import com.inscoper.nbo.ProjectID_Type;
import com.inscoper.nbo.UniversallyUniqueIdentifier_Type;

public class ChoiceElements {
    public static void main(String[] args) {
        // Step 1: A choice of repeated alternatives
        // Pixels may carry BinData, TiffData, NGFFData, or MetadataOnly children. The
        // choice object is filled first, then assigned to its parent.
        Pixels_Choice pixelsChoice = new Pixels_Choice();
        System.out.println(pixelsChoice.containsBinData() + " " + pixelsChoice.containsTiffData());

        BinDataVector binDataList = new BinDataVector();
        for (String compression : new String[] {"zlib", "bzip2"}) {
            BinData binData = new BinData();
            binData.setCompression(compression);
            binData.setBigEndian(true);
            NonNegativeLong_Type length = new NonNegativeLong_Type();
            length.set(12);
            binData.setLength(length);
            binDataList.add(binData);
        }

        pixelsChoice.addBinData(binDataList);
        System.out.println(pixelsChoice.containsBinData() + " " + pixelsChoice.containsTiffData());

        Pixels pixels = new Pixels();
        pixels.setpixels_choice(pixelsChoice);

        // Pixels declares required attributes of its own, which a validated export would
        // demand. Only the choice matters here, so export without validation; see the
        // Describe an Acquired Image example for a complete, valid Pixels element.
        System.out.println(pixels.toXmlString(false));

        // Step 2: A choice of exclusive alternatives
        // OME holds either a BinaryOnly reference or a full metadata document. Setting
        // one alternative clears the other.
        OME_Choice omeChoice = new OME_Choice();
        System.out.println(omeChoice.isBinaryOnly() + " " + omeChoice.isOME_Choice_1());

        OME_Choice_InlineBinaryOnly binaryOnly = new OME_Choice_InlineBinaryOnly();
        UniversallyUniqueIdentifier_Type uuid = new UniversallyUniqueIdentifier_Type();
        uuid.set("urn:uuid:3e450fae-b8f2-4d35-aa54-702168b2487f");
        binaryOnly.setUUID(uuid);
        binaryOnly.setMetadataFile("metadata.ome.xml");
        omeChoice.setBinaryOnly(binaryOnly);
        System.out.println(omeChoice.isBinaryOnly() + " " + omeChoice.isOME_Choice_1());

        OME_Choice_InlineOME_Choice_1 fullDocument = new OME_Choice_InlineOME_Choice_1();
        Project project = new Project();
        ProjectID_Type projectId = new ProjectID_Type();
        projectId.set("Project:1");
        project.setID(projectId);
        project.setName("Mitosis screen");
        fullDocument.addToProject_List(project);
        omeChoice.setOME_Choice_1(fullDocument);
        System.out.println(omeChoice.isBinaryOnly() + " " + omeChoice.isOME_Choice_1());

        OME ome = new OME();
        ome.setome_choice(omeChoice);
        System.out.println(ome.toXmlString());
    }
}

Populate inline elements

import inscoper_nbo

# Step 1: Recognize an inline type by its name
# An element declared inside Screen produces the class Screen_Inline<ElementName>,
# which exists only in the context of that parent.
screen = inscoper_nbo.Screen()

screen_id = inscoper_nbo.ScreenID_Type()
screen_id.set("Screen:1")
screen.setID(screen_id)

description = inscoper_nbo.Screen_InlineDescription()
description.set("Plate screen, 3 plates")
screen.setDescription(description)

# Step 2: Repeated inline elements behave like any other list
for plate in ("Plate:1", "Plate:2"):
    plate_ref = inscoper_nbo.Screen_InlinePlateRef()
    plate_id = inscoper_nbo.PlateID_Type()
    plate_id.set(plate)
    plate_ref.setID(plate_id)
    screen.addToPlateRef_List(plate_ref)

print(screen.toXmlString())

# Step 3: Inline elements are read back from XML into the same classes
screen.fromXmlString(
    """<?xml version="1.0"?>
<Screen ID="Screen:03">
  <Description>Rewritten from XML</Description>
  <PlateRef ID="Plate:01" />
  <PlateRef ID="Plate:02" />
  <PlateRef ID="Plate:03" />
</Screen>"""
)

print(screen.getDescription().get())
print([plate_ref.getID().get() for plate_ref in screen.getPlateRef_List()])

#include <NBO/NBO/PlateID_Type.h>
#include <NBO/NBO/Screen.h>
#include <NBO/NBO/ScreenID_Type.h>

#include <iostream>
#include <memory>

using namespace Inscoper;

int main() {
    // Step 1: Recognize an inline type by its name
    // An element declared inside Screen produces the nested class
    // Screen::Inline<ElementName>, which exists only in the context of that parent.
    NBO::Screen screen;

    NBO::ScreenID_TypePtr screenId = std::make_shared<NBO::ScreenID_Type>();
    screenId->set("Screen:1");
    screen.setID(screenId);

    NBO::Screen::InlineDescriptionPtr description =
        std::make_shared<NBO::Screen::InlineDescription>();
    description->set("Plate screen, 3 plates");
    screen.setDescription(description);

    // Step 2: Repeated inline elements behave like any other list
    for (const std::string &plate : {"Plate:1", "Plate:2"}) {
        NBO::Screen::InlinePlateRefPtr plateRef = std::make_shared<NBO::Screen::InlinePlateRef>();
        NBO::PlateID_TypePtr plateId = std::make_shared<NBO::PlateID_Type>();
        plateId->set(plate);
        plateRef->setID(plateId);
        screen.addToPlateRef_List(plateRef);
    }

    std::cout << screen.toXmlString() << std::endl;

    // Step 3: Inline elements are read back from XML into the same classes
    screen.fromXmlString(R"(<?xml version="1.0"?>
<Screen ID="Screen:03">
  <Description>Rewritten from XML</Description>
  <PlateRef ID="Plate:01" />
  <PlateRef ID="Plate:02" />
  <PlateRef ID="Plate:03" />
</Screen>)");

    std::cout << screen.getDescription()->get() << std::endl;
    for (const auto &plateRef : screen.getPlateRef_List()) {
        std::cout << plateRef->getID()->get() << " ";
    }
    std::cout << std::endl;

    return 0;
}

import com.inscoper.nbo.PlateID_Type;
import com.inscoper.nbo.Screen;
import com.inscoper.nbo.ScreenID_Type;
import com.inscoper.nbo.Screen_InlineDescription;
import com.inscoper.nbo.Screen_InlinePlateRef;

public class InlineElements {
    public static void main(String[] args) {
        // Step 1: Recognize an inline type by its name
        // An element declared inside Screen produces the class
        // Screen_Inline<ElementName>, which exists only in the context of that parent.
        Screen screen = new Screen();

        ScreenID_Type screenId = new ScreenID_Type();
        screenId.set("Screen:1");
        screen.setID(screenId);

        Screen_InlineDescription description = new Screen_InlineDescription();
        description.set("Plate screen, 3 plates");
        screen.setDescription(description);

        // Step 2: Repeated inline elements behave like any other list
        for (String plate : new String[] {"Plate:1", "Plate:2"}) {
            Screen_InlinePlateRef plateRef = new Screen_InlinePlateRef();
            PlateID_Type plateId = new PlateID_Type();
            plateId.set(plate);
            plateRef.setID(plateId);
            screen.addToPlateRef_List(plateRef);
        }

        System.out.println(screen.toXmlString());

        // Step 3: Inline elements are read back from XML into the same classes
        screen.fromXmlString("""
                <?xml version="1.0"?>
                <Screen ID="Screen:03">
                  <Description>Rewritten from XML</Description>
                  <PlateRef ID="Plate:01" />
                  <PlateRef ID="Plate:02" />
                  <PlateRef ID="Plate:03" />
                </Screen>""");

        System.out.println(screen.getDescription().get());
        for (Screen_InlinePlateRef plateRef : screen.getPlateRef_List()) {
            System.out.print(plateRef.getID().get() + " ");
        }
        System.out.println();
    }
}