次の方法で共有


DTDL パーサー ライブラリを使用してモデルを解析および検証する

この記事では、.NET パーサー ライブラリを使用して Azure Digital Twins モデルを解析および検証する方法について説明します。

Azure Digital Twins のモデルは、JSON-LD ベースの Digital Twins Definition 言語 (DTDL) を使用して定義されます。

モデルを作成した後、Azure Digital Twins インスタンスにアップロードする前に、モデルをオフラインで検証することをお勧めします。

モデルの検証に役立つ、.NET クライアント側の DTDL 解析ライブラリが NuGet で提供されています: DTDLParser。 C# コードのパーサー ライブラリを直接使用できます。 GitHub の DTDLParserResolveSample でパーサーの使用例を確認することもできます。

.NET パーサー ライブラリについて

DTDLParser ライブラリは、DTDL 定義へのモデル アクセスを提供し、基本的には DTDL の C# リフレクションと同等の機能を果たします。 このライブラリは、Azure Digital Twins SDK とは別に使用できます。特に、ビジュアル エディターまたはテキスト エディターでの DTDL 検証に使用できます。 これは、モデル定義ファイルをサービスにアップロードする前に、モデル定義ファイルが有効であることを確認するのに役立ちます。

パーサー ライブラリを使用するには、一連の DTDL ドキュメントを提供します。 通常、これらのモデル ドキュメントはサービスから取得しますが、クライアントが最初にサービスにアップロードする責任がある場合は、ローカルで使用することもできます。

パーサーを使用するための一般的なワークフローを次に示します。

  1. サービスから一部またはすべての DTDL ドキュメントを取得します。
  2. 返されたメモリ内 DTDL ドキュメントをパーサーに渡します。
  3. パーサーは、渡されたドキュメントのセットを検証し、詳細なエラー情報を返します。 この機能は、エディターのシナリオで役立ちます。
  4. パーサー API を使用して、ドキュメント セットに含まれるモデルの分析を続行します。

パーサーの機能は次のとおりです。

  • 実装されているすべてのモデル インターフェイス (インターフェイスの extends セクションの内容) を取得します。
  • モデルで宣言されているすべてのプロパティ、テレメトリ、コマンド、コンポーネント、リレーションシップを取得します。 このコマンドは、これらの定義に含まれるすべてのメタデータも取得し、継承 (extends セクション) を考慮します。
  • すべての複雑なモデル定義を取得します。
  • モデルが別のモデルから割り当て可能かどうかを判断します。

IoT プラグ アンド プレイ デバイスでは、小さな構文バリアントを使用して機能を記述します。 この構文バリアントは、Azure Digital Twins で使用される DTDL の意味的に互換性のあるサブセットです。 パーサー ライブラリを使用する場合、デジタル ツインの DTDL を作成するために使用された構文バリアントを把握する必要はありません。 パーサーは、IoT プラグ アンド プレイ構文と Azure Digital Twins 構文の両方に対して、常に同じモデルを既定で返します。

パーサー ライブラリを使用したコード

パーサー ライブラリは、独自のアプリケーションでモデルを検証したり、動的なモデル駆動型 UI、ダッシュボード、レポートを生成したりするために直接使用できます。

次のパーサー コード例をサポートするには、Azure Digital Twins インスタンスで定義されている複数のモデルを検討してください。

[
    {
      "@context": "dtmi:dtdl:context;3",
      "@id": "dtmi:com:contoso:coffeeMaker;1",
      "@type": "Interface",
      "contents": [
        {
          "@type": "Component",
          "name": "coffeeMaker",
          "schema": "dtmi:com:contoso:coffeeMakerInterface;1"
        }
      ]
    },
    {
      "@context": "dtmi:dtdl:context;3",
      "@id": "dtmi:com:contoso:coffeeMakerInterface;1",
      "@type": "Interface",
      "contents": [
        {
          "@type": "Property",
          "name": "waterTemp",
          "schema": "double"
        }
      ]
    },
    {
      "@context": "dtmi:dtdl:context;3",
      "@id": "dtmi:com:contoso:coffeeBar;1",
      "@type": "Interface",
      "contents": [
        {
          "@type": "Relationship",
          "name": "foo",
          "target": "dtmi:com:contoso:coffeeMaker;1"
        },
        {
          "@type": "Property",
          "name": "capacity",
          "schema": "integer"
        }
      ]
    }
  ]

次のコードは、パーサー ライブラリを使用して C# でこれらの定義を反映する方法の例を示しています。

using Azure;
using Azure.DigitalTwins.Core;
using DTDLParser;
using DTDLParser.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DigitalTwins_Samples
{
    public static class ListExtensions
    {
        public static async IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IEnumerable<T> input)
        {
            foreach (var value in input)
            {
                yield return value;
            }
            await Task.Yield();
        }
    }

    public class ParseModelsSample
    {
        public async Task ParseDemoAsync(DigitalTwinsClient client)
        {
            try
            {
                AsyncPageable<DigitalTwinsModelData> mdata = client.GetModelsAsync(new GetModelsOptions { IncludeModelDefinition = true });
                var models = new List<string>();
                await foreach (DigitalTwinsModelData md in mdata)
                    models.Add(md.DtdlModel);
                var parser = new ModelParser();
                IReadOnlyDictionary<Dtmi, DTEntityInfo> dtdlOM = await parser.ParseAsync(models.AsAsyncEnumerable());

                var interfaces = new List<DTInterfaceInfo>();
                IEnumerable<DTInterfaceInfo> ifenum =
                    from entity in dtdlOM.Values
                    where entity.EntityKind == DTEntityKind.Interface
                    select entity as DTInterfaceInfo;
                interfaces.AddRange(ifenum);
                foreach (DTInterfaceInfo dtif in interfaces)
                {
                    PrintInterfaceContent(dtif, dtdlOM);
                }
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"Failed due to {ex}");
                throw;
            }
        }

        public void PrintInterfaceContent(DTInterfaceInfo dtif, IReadOnlyDictionary<Dtmi, DTEntityInfo> dtdlOM, int indent = 0)
        {
            var sb = new StringBuilder();
            for (int i = 0; i < indent; i++) sb.Append("  ");
            Console.WriteLine($"{sb}Interface: {dtif.Id} | {dtif.DisplayName}");
            IReadOnlyDictionary<string, DTContentInfo> contents = dtif.Contents;

            foreach (DTContentInfo item in contents.Values)
            {
                switch (item.EntityKind)
                {
                    case DTEntityKind.Property:
                        DTPropertyInfo pi = item as DTPropertyInfo;
                        Console.WriteLine($"{sb}--Property: {pi.Name} with schema {pi.Schema}");
                        break;
                    case DTEntityKind.Relationship:
                        DTRelationshipInfo ri = item as DTRelationshipInfo;
                        Console.WriteLine($"{sb}--Relationship: {ri.Name} with target {ri.Target}");
                        break;
                    case DTEntityKind.Telemetry:
                        DTTelemetryInfo ti = item as DTTelemetryInfo;
                        Console.WriteLine($"{sb}--Telemetry: {ti.Name} with schema {ti.Schema}");
                        break;
                    case DTEntityKind.Component:
                        DTComponentInfo ci = item as DTComponentInfo;
                        Console.WriteLine($"{sb}--Component: {ci.Id} | {ci.Name}");
                        DTInterfaceInfo component = ci.Schema;
                        PrintInterfaceContent(component, dtdlOM, indent + 1);
                        break;                
                }
            }
        }
    }
}

次のステップ

モデルの作成が完了したら、Azure Digital Twins Models API を使用してモデルをアップロードする (およびその他の管理操作を行う) 方法を確認します。