The ModelParser
class is used to determine whether one or more DTDL models are valid, to identify specific modeling errors, and to enable inspection of model contents.
This tutorial walks through the last of these uses: how to access elements and properties in the object model.
A synchronous version of this tutorial is also available.
To parse a DTDL model, you need to instantiate a ModelParser
.
No arguments are required.
var modelParser = new ModelParser();
The DTDL language is syntactically JSON.
The ModelParser
expects a single string or an asynchronous enumeration of strings.
The single string or each value in the enumeration is JSON text of a DTDL model.
string jsonText =
@"{
""@context"": ""dtmi:dtdl:context;3"",
""@id"": ""dtmi:example:anInterface;1"",
""@type"": ""Interface"",
""contents"": [
{
""@type"": ""Property"",
""name"": ""expectedDistance"",
""schema"": ""double""
},
{
""@type"": ""Telemetry"",
""name"": ""currentDistance"",
""schema"": ""double""
}
]
}";
The main asynchronous method on the ModelParser
is ParseAsync()
.
One argument is required, which can be either a string or an asynchronous enumeration of strings containing the JSON text to parse as DTDL.
var parseTask = modelParser.ParseAsync(jsonText);
The return value is a Task
, whose completion must be awaited before proceeding.
If the submitted model is complete and valid, no exception will be thrown.
Proper code should catch and process exceptions as shown in other tutorials such as this one, but for simplicity the present tutorial omits exception handling.
parseTask.Wait();
IReadOnlyDictionary<Dtmi, DTEntityInfo> objectModel = parseTask.Result;
The object model is a collection of objects in a class hierarchy rooted at DTEntityInfo
.
All DTDL elements derive from the DTDL abstract type Entity, and each DTDL type has a corresponding C# class whose name has a prefix of "DT" (for Digital Twins) and a suffix of "Info".
The elements in the object model are indexed by their identifiers, which have type Dtmi
. The following snippet displays the identifiers of all elements in the object model:
Console.WriteLine($"{objectModel.Count} elements in model:");
foreach (KeyValuePair<Dtmi, DTEntityInfo> modelElement in objectModel)
{
Console.WriteLine(modelElement.Key);
}
For the JSON text above, this snippet displays:
4 elements in model:
dtmi:example:anInterface:_contents:__expectedDistance;1
dtmi:example:anInterface:_contents:__currentDistance;1
dtmi:example:anInterface;1
dtmi:dtdl:instance:Schema:double;2
Of these four identifiers, only dtmi:example:anInterface;1 is present in the DTDL source model.
The identifiers for the contents named "expectedDistance" and "currentDistance" are auto-generated by the ModelParser
following rules that guarantee their uniqueness.
The identifier dtmi:dtdl:instance:Schema:double;2 represents an element in the DTDL language model for the schema 'double', as can be seen by using the ModelParser.GetTermOrUri()
static method:
Console.WriteLine(ModelParser.GetTermOrUri(new Dtmi("dtmi:dtdl:instance:Schema:double;2")));
This snippet displays:
double
An individual element can be looked up in the object model by its identifier:
var anInterfaceId = new Dtmi("dtmi:example:anInterface;1");
var anInterface = (DTInterfaceInfo)objectModel[anInterfaceId];
The Interface in the DTDL model above has two 'contents' values, and these can been seen in the object model property Contents
on the DTInterfaceInfo
class:
foreach (KeyValuePair<string, DTContentInfo> contentElement in anInterface.Contents)
{
Console.WriteLine($"name \"{contentElement.Key}\" => {contentElement.Value.Id}");
}
This snippet displays:
name "expectedDistance" => dtmi:example:anInterface:_contents:__expectedDistance;1
name "currentDistance" => dtmi:example:anInterface:_contents:__currentDistance;1
The DTDL type of each element is expressed via the property EntityKind
on the DTEntityInfo
base class, which has type enum DTEntityKind
:
foreach (KeyValuePair<string, DTContentInfo> contentElement in anInterface.Contents)
{
Console.WriteLine($"name \"{contentElement.Key}\" => {contentElement.Value.EntityKind}");
}
This snippet displays:
name "expectedDistance" => Property
name "currentDistance" => Telemetry
Each of the content values can be accessed either by name via the Contents
property on DTInterfaceInfo
:
string expectedDistanceName = "expectedDistance";
var expectedDistance = (DTPropertyInfo)anInterface.Contents[expectedDistanceName];
Or by identifier via the object model:
Dtmi currentDistanceId = new Dtmi("dtmi:example:anInterface:_contents:__currentDistance;1");
var currentDistance = (DTTelemetryInfo)objectModel[currentDistanceId];
Both approaches yield the indicated element's object, from which other properties can be accessed, such as the schema of each content:
Console.WriteLine($"expectedDistance schema is {expectedDistance.Schema.Id}");
Console.WriteLine($"currentDistance schema is {currentDistance.Schema.Id}");
This snippet displays:
expectedDistance schema is dtmi:dtdl:instance:Schema:double;2
currentDistance schema is dtmi:dtdl:instance:Schema:double;2