ElementType.fromJson constructor

ElementType.fromJson(
  1. Map<String, dynamic> json
)

Implementation

factory ElementType.fromJson(Map<String, dynamic> json) {
  // Based on original code, it seems `description` and `properties` are at top level:
  // {
  //   "type": "object",
  //   "description": "...",
  //   "required": [...],
  //   "properties": {
  //     "tagName": {
  //       "type": "object",
  //       "required": [...],
  //       "properties": { ... }
  //     }
  //   }
  // }

  final description = json["description"] as String?;

  final propertiesMap = json["properties"] as Map;

  // The Python code picks up the first key in properties as the tagName and uses its properties
  // structure:
  // "properties": {
  //   "tagName": {
  //       "type": "object",
  //       ...
  //       "properties": {
  //          "prop_name": { "description":..., "type": ... or "array" ... }
  //       }
  //   }
  // }

  if (propertiesMap.isEmpty) {
    throw MeshSchemaValidationException("Invalid schema json: no properties found");
  }

  // In Python code, it uses `for k, type_json in json["properties"].items()` then returns inside.
  // So effectively, we take the first entry as the element definition.
  final firstEntry = propertiesMap.entries.first;
  final tagName = firstEntry.key;
  final typeJson = firstEntry.value as Map;

  final propMap = typeJson["properties"] as Map;
  final properties = <ElementProperty>[];

  propMap.forEach((propName, p) {
    final pMap = p as Map;
    final propDescription = pMap["description"] as String?;
    final pType = pMap["type"];

    if (pType == "array") {
      if (pMap["items"] != null && pMap["items"] is Map && pMap["items"]["anyOf"] != null) {
        // handle ChildProperty
        final items = pMap["items"] as Map;

        final anyOf = items["anyOf"] as List<dynamic>;
        final childTagNames = <String>[];

        for (var refObj in anyOf) {
          final refMap = refObj as Map;
          final refStr = refMap["\$ref"] as String;
          const prefix = "#/\$defs/";
          final childTagName = refStr.startsWith(prefix) ? refStr.substring(prefix.length) : refStr;
          childTagNames.add(childTagName);
        }

        properties.add(ChildProperty(name: propName, description: propDescription, childTagNames: childTagNames));
      } else if (pMap["prefixItems"] != null) {
        final anyOf = pMap["prefixItems"] as List<dynamic>;
        final childTagNames = <String>[];

        for (var refObj in anyOf) {
          final refMap = refObj as Map;
          final refStr = refMap["\$ref"] as String;
          const prefix = "#/\$defs/";
          final childTagName = refStr.startsWith(prefix) ? refStr.substring(prefix.length) : refStr;
          childTagNames.add(childTagName);
        }

        properties.add(ChildProperty(name: propName, description: propDescription, childTagNames: childTagNames, ordered: true));
      } else {
        throw MeshSchemaValidationException("Invalid array type encountered");
      }
    } else {
      // handle ValueProperty
      // pType should be a string matching SimpleValue
      final valTypeStr = pType as String;
      final valType = SimpleValue.fromString(valTypeStr);
      if (valType == null) {
        throw MeshSchemaValidationException("Invalid value type: $valTypeStr");
      }

      final enumValue = pMap["enum"] as List<dynamic>?;
      properties.add(ValueProperty(name: propName, description: propDescription, type: valType, enumValues: enumValue));
    }
  });

  return ElementType(tagName: tagName, description: description, properties: properties);
}