Repository navigation
ReqIFSharp Extensions
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.Extensionsusing ReqIFSharp.Extensions.ReqIFExtensions; // the extension methods
using ReqIFSharp.Extensions.Services; // ReqIFLoaderServiceThe extension methods use the
ReqIFContentproperty of elements to find related elements. Documents read byReqIFDeserializerhave it set. On a document you build yourself, set it on the spec types and spec elements you query, see Writing ReqIF.
| 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));
}| 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}");
}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());| 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'
...
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.
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.
StringExtensions has Base64Encode() and Base64Decode() for UTF-8 strings, in the ReqIFSharp.Extensions namespace.
copyright @ Starion Group S.A.