DTDL 파서 라이브러리를 사용하여 모델 구문 분석 및 유효성 검사
이 문서에서는 .NET 파서 라이브러리를 사용하여 Azure Digital Twins 모델을 구문 분석하고 유효성을 검사하는 방법을 설명합니다.
Azure Digital Twins의 모델은 JSON-LD 기반 DTDL(디지털 트윈 정의 언어)을 사용하여 정의됩니다.
모델을 만든 후에는 Azure Digital Twins 인스턴스에 업로드하기 전에 오프라인으로 모델의 유효성을 검사하는 것이 좋습니다.
모델의 유효성을 검사하는 데 도움이 되도록 NuGet: DTDLParser에 .NET 클라이언트 측 DTDL 구문 분석 라이브러리가 제공됩니다. C# 코드에서 직접 파서 라이브러리를 사용할 수 있습니다. GitHub의 DTDLParserResolveSample에서 파서의 샘플 사용을 볼 수도 있습니다.
.NET 파서 라이브러리 정보
DTDLParser 라이브러리는 DTDL 정의에 대한 모델 액세스를 제공하며, 기본적으로 DTDL에 대한 C# 리플렉션과 동등한 역할을 합니다. 이 라이브러리는 특히 시각적 개체 또는 텍스트 편집기에서 DTDL 유효성 검사를 위해 Azure Digital Twins SDK와 별개로 사용할 수 있습니다. 모델 정의 파일을 서비스에 업로드하기 전에 해당 파일이 유효한지 확인하는 데 유용합니다.
파서 라이브러리 사용을 위해 DTDL 문서 집합을 제공합니다. 일반적으로 서비스에서 이러한 모델 문서를 검색하지만 클라이언트에서 먼저 서비스에 업로드해야 하는 경우 로컬에서 검색이 가능하도록 할 수 있습니다.
다음은 파서 사용에 대한 일반적인 워크플로입니다.
- 서비스에서 일부 또는 모든 DTDL 문서를 검색합니다.
- 반환된 메모리 내 DTDL 문서를 파서에 전달합니다.
- 파서에서 전달된 문서 집합의 유효성을 검사하고, 자세한 오류 정보를 반환합니다. 이 기능은 편집기 시나리오에서 유용합니다.
- 파서 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 DigitalTwinsModels API를 사용하여 모델을 업로드하는 방법(및 다른 관리 작업을 수행하는 방법)을 참조하세요.