Skip to content

ReqIFSharp Extensions

samatrhea edited this page Oct 18, 2026 · 1 revision

ReqIFSharp.Extensions is a separate NuGet package on top of ReqIFSharp. It adds extension methods that answer common questions about a ReqIF document, and a ReqIFLoaderService that loads a document and gives access to its attachments. The ReqIF Viewer is built on it.

dotnet add package ReqIFSharp.Extensions
using ReqIFSharp.Extensions.ReqIFExtensions;   // the extension methods
using ReqIFSharp.Extensions.Services;          // ReqIFLoaderService

The extension methods use the ReqIFContent property of elements to find related elements. Documents read by ReqIFDeserializer have it set. On a document you build yourself, set it on the spec types and spec elements you query, see Writing ReqIF.

Navigating specifications

Method Returns
specification.QueryAllContainedSpecObjects() every spec object in the specification, at any depth, in document order
specification.QueryAllContainedSpecHierarchies() every SpecHierarchy node in the specification, at any depth
specHierarchy.QueryAllContainedSpecHierarchies() every node below a node
specification.QueryAttributeDefinitions() the attribute definitions that have a value in at least one spec object of the specification: the columns of a table view. The order is not defined.
specObject.QueryContainerSpecifications() the specifications that contain a spec object

Export one specification as a table:

var specification = reqif.CoreContent.Specifications.First();
var columns = specification.QueryAttributeDefinitions().ToList();

Console.WriteLine(string.Join(" | ", columns.Select(c => c.LongName)));

foreach (var specObject in specification.QueryAllContainedSpecObjects())
{
    var cells = columns.Select(column =>
        specObject.Values.FirstOrDefault(v => v.AttributeDefinition == column)?.QueryFormattedValue() ?? "");

    Console.WriteLine(string.Join(" | ", cells));
}

Trace links

Method Returns
specObject.QuerySpecRelations() every relation in which the spec object is the source or the target
specRelationType.QueryReferencingSpecRelations() every relation of that type
foreach (var relation in specObject.QuerySpecRelations())
{
    var direction = relation.Source == specObject ? "outgoing" : "incoming";
    var other = relation.Source == specObject ? relation.Target : relation.Source;

    Console.WriteLine($"{direction} {relation.Type.LongName}: {other.LongName}");
}

Where is a type used?

Useful for impact analysis, or to clean up unused types:

Method Returns
datatypeDefinition.QueryReferencingAttributeDefinitions() the attribute definitions that use the datatype
specObjectType.QueryReferencingSpecObjects() the spec objects of that type
specificationType.QueryReferencingSpecifications() the specifications of that type
relationGroupType.QueryReferencingRelationGroups() the relation groups of that type
var unused = reqif.CoreContent.DataTypes.Where(d => !d.QueryReferencingAttributeDefinitions().Any());

Display text

Method Returns
attributeValue.QueryFormattedValue() any attribute value as a string, formatted with the invariant culture. XHTML gives the XHTML markup; use ExtractUnformattedTextFromValue() for plain text. Enumerations give the Properties.OtherContent of the selected literals, separated by ;; use Values.Select(v => v.LongName) for their names.
attributeDefinition.QueryDefaultValueAsFormattedString() the default value as a string, or "NOT SET"
attributeDefinition.QueryDatatypeName(), datatypeDefinition.QueryDatatypeName() Boolean, Date, Enumeration, Integer, Real, String or XHTML
identifiable.QueryTypeDisplayName() a readable name of the kind of element, such as Spec Object or Enumeration Attribute Definition
foreach (var specType in reqif.CoreContent.SpecTypes.OfType<SpecObjectType>())
{
    foreach (var attribute in specType.SpecAttributes)
    {
        Console.WriteLine($"{specType.LongName}.{attribute.LongName}: {attribute.QueryDatatypeName()}, default '{attribute.QueryDefaultValueAsFormattedString()}'");
    }
}
Requirement Type.ID: String, default 'TBD'
Requirement Type.Bool: Boolean, default 'True'
Requirement Type.Date: Date, default 'August 20, 1976'
Requirement Type.Int: Integer, default '6'
...

Attachments with the loader service

ReqIFLoaderService loads a ReqIF file or archive, keeps a copy of the source, and reads attachments from it on request. It caches what it has read. This is what you want in an interactive application, where the user opens a document and then browses it.

using var loader = new ReqIFLoaderService(new ReqIFDeserializer());

loader.ReqIfChanged += (sender, reqifs) => Console.WriteLine($"Loaded {reqifs.Count()} document(s)");

await using (var stream = File.OpenRead("requirements.reqifz"))
{
    await loader.LoadAsync(stream, SupportedFileExtensionKind.Reqifz, cancellationToken);
}

var reqif = loader.ReqIFData.First();

The loader copies the stream, so you can close it after LoadAsync. The stream must be seekable.

Member Purpose
LoadAsync(stream, kind, token) load a document; replaces anything loaded before
ReqIFData the loaded ReqIF objects
ReqIfChanged event raised after LoadAsync and Reset
QueryDataAsync(externalObject, token) an attachment as a data: URI, such as data:image/png;base64,iVBORw0KG...
GetSourceStreamAsync(token) a copy of the loaded source stream
Reset() forget the loaded document and clear the cache

Find attachments with QueryExternalObjects(), on the whole content or on one element, and get them all at once with QueryBase64PayloadsAsync:

var externalObjects = reqif.CoreContent.QueryExternalObjects();       // all attachments in the document

foreach (var (externalObject, dataUri) in await specObject.QueryBase64PayloadsAsync(loader, cancellationToken))
{
    Console.WriteLine($"{externalObject.Uri} ({externalObject.MimeType})");
    // dataUri can be used directly as the src of an <img> element
}

externalObject.CreateUrl() builds a relative URL of the form /reqif/{header identifier}/externalobject/{base64 of the uri}. The ReqIF Viewer uses it to route attachment requests; use it if your web application follows the same scheme.

Using it in a web application

Register the service per user session, for example scoped in Blazor Server, so every user has their own loaded document:

builder.Services.AddSingleton<IReqIFDeSerializer, ReqIFDeserializer>();
builder.Services.AddScoped<IReqIFLoaderService, ReqIFLoaderService>();

ReqIFLoaderService implements IDisposable. The container disposes it at the end of the scope.

Base64 helpers

StringExtensions has Base64Encode() and Base64Decode() for UTF-8 strings, in the ReqIFSharp.Extensions namespace.

Clone this wiki locally