digitaltwinconsortium / DTDLParser

Digital Twin Definition Language parser for .NET
MIT License
14 stars 8 forks source link

Flatten Model #116

Closed rquackenbush closed 1 year ago

rquackenbush commented 1 year ago

We're currently working on a project where we need the "effective" or "flattened" model definition. In other words, given the following models:

vehicle.json

{
  "@id": "dtmi:example:Vehicle;1",
  "@type": "Interface",
  "displayName": "Vehicle",
  "contents": [
    {
      "@type": "Property",
      "name": "make",
      "schema": "string"
    },
    {
      "@type": "Property",
      "name": "model",
      "schema": "string"
    }
  ],
  "@context": "dtmi:dtdl:context;2"
}

car.json

{
  "@id": "dtmi:example:Car;1",
  "@type": "Interface",
  "displayName": "Car",
  "contents": [
    {
      "@type": "Property",
      "name": "tireSize",
      "schema": "string"
    },
    {
      "@type": "Property",
      "name": "numberOfDoors",
      "schema": "int"
    }
  ],
  "@context": "dtmi:dtdl:context;2",
  "extends": "dtmi:example:Vehicle;1"
}

We want the effective or flatted schema for dtmi:example:Car;1 - which would be:

{
  "@id": "dtmi:example:Car;1",
  "@type": "Interface",
  "displayName": "Car",
  "contents": [
    {
      "@type": "Property",
      "name": "make",
      "schema": "string"
    },
    {
      "@type": "Property",
      "name": "model",
      "schema": "string"
    },
    {
      "@type": "Property",
      "name": "tireSize",
      "schema": "string"
    },
    {
      "@type": "Property",
      "name": "numberOfDoors",
      "schema": "int"
    }
  ],
  "@context": "dtmi:dtdl:context;2",
  "extends": "dtmi:example:Vehicle;1"
}

We're planning on doing this with json manipulation, but this might be useful to other folks as well.

Proposed Solution

Perhaps add something like a FlattenInterface extension method:

public static class Extensions
{
   public static DTInterfaceInfo FlattenInterface(this IReadOnlyDictionary<Dtmi, DtmiEntityInfo> parsed, Dtmi dtmi)
   {
      //TODO: Magic
   }
}
var jsonTexts = new string[] 
{
  File.ReadAllText("vehicle.json"),
  File.ReadAllText("car.json")
}

var parser = new ModelParser();
var result = parser.Parse(jsonTexts);

DTInterfaceInfo flattened = result.FlattenInterface(new Dtmi("dtmi:example:Car;1"));

var json = model.GetJsonDLText();
rido-min commented 1 year ago

Hi @rquackenbush

The result of Parse already flattens all the elements from both interfaces. If you want to enumerate just the interfaces, you can filter the results:

var result = parser.Parse(jsonTexts);

foreach (DTInterfaceInfo item in result
    .Where(r => r.Value.EntityKind == DTEntityKind.Interface)
    .Select( i => (DTInterfaceInfo)i.Value))
{
    Console.WriteLine(item.Id);
}

If you want to just enumerate the properties, the .Properties already exposes the flattened list of properties.

rquackenbush commented 1 year ago

@rido-min oh wow - thanks for that response. I hadn't thought about approaching it from that angle.

The last piece - how do we turn those results into json? The constructors for the model objects all appear to be internal:

For example: DTInterfaceInfo

rido-min commented 1 year ago

ParseToJson will return the JSON serialized view of the dictionary.

also, check out the DTDLParserJSInteropSample to see how to consume the JSON using the provided typings.

rquackenbush commented 1 year ago

That works! Thank you!