Repository: biolink/biolinkml

Issue:
json-schema 
```yaml
id: http://example.org/sample/types

prefixes:
  biolinkml: https://w3id.org/biolink/biolinkml/

imports:
  - biolinkml:types

types:
  yearCount:
    base: int
    uri: xsd:int

classes:
  c:
    slots:
      - id
      - age in years
      - scores
      - has prop
      - has d
      - has ds
      - children
      - parent
  d:
    slots:
      - id


slots:

  id:
    identifier: true

  scores:
    range: float
    multivalued: true
  has prop:
    range: boolean
  age in years:
    range: yearCount
  has d:
    range: d
    multivalued: false
    inlined: true
  has ds:
    range: d
    multivalued: true
    inlined: true
  children:
    range: c
    multivalued: true
  parent:
    range: c
    multivalued: false
```

current json output:

```json
      "C": {
         "description": "",
         "properties": {
            "age_in_years": {
               "type": "string"
            },
            "children": {
               "items": {
                  "type": "string"
               },
               "type": "array"
            },
            "has_d": {
               "type": "string"
            },
            "has_ds": {
               "items": {
                  "type": "string"
               },
               "type": "array"
            },
            "has_prop": {
               "type": "string"
            },
            "id": {
               "required": true,
               "type": "string"
            },
            "parent": {
               "type": "string"
            },
            "scores": {
               "items": {
                  "type": "string"
               },
               "type": "array"
            }
         },
         "title": "C",
         "type": "object"
```

There should be a `$ref` for any inlined reference to D

Discussion:

Patch:
diff --git a/biolinkml/generators/jsonschemagen.py b/biolinkml/generators/jsonschemagen.py
index 6af50246..77d77de1 100644
--- a/biolinkml/generators/jsonschemagen.py
+++ b/biolinkml/generators/jsonschemagen.py
@@ -57,14 +57,25 @@ class JsonSchemaGenerator(Generator):
         self.schemaobj.definitions[camelcase(cls.name)] = self.clsobj
 
     def visit_class_slot(self, cls: ClassDefinition, aliased_slot_name: str, slot: SlotDefinition) -> None:
-        if slot.range in self.schema.classes or slot.range in self.schema.types:
+        if slot.range in self.schema.classes and slot.inlined:
             rng = f"#/definitions/{camelcase(slot.range)}"
+        elif slot.range in self.schema.types:
+            rng = self.schema.types[slot.range].base
         else:
+            # note we assume string for non-lined complex objects
             rng = "string"
-        #slotrange = camelcase(
-        #    slot.range) if slot.range in self.schema.classes or slot.range in self.schema.types else "String"
 
-        if self.inline or slot.inlined:
+        # translate to json-schema builtins
+        if rng == 'int':
+            rng = 'integer'
+        elif rng == 'Bool':
+            rng = 'boolean'
+        elif rng == 'str':
+            rng = 'string'
+        elif rng == 'float' or rng == 'double':
+            rng = 'number'
+
+        if slot.inlined:
             # If inline we have to include redefined slots
             ref = JsonObj()
             ref['$ref'] = rng
@@ -73,7 +84,10 @@ class JsonSchemaGenerator(Generator):
             else:
                 prop = ref
         else:
-            prop = JsonObj(type="string") #TODO
+            if slot.multivalued:
+                prop = JsonObj(type="array", items={'type':rng})
+            else:
+                prop = JsonObj(type=rng)
         if slot.description:
             prop.description = slot.description
         if slot.required:
@@ -83,15 +97,6 @@ class JsonSchemaGenerator(Generator):
         if self.topCls is not None and camelcase(self.topCls) == camelcase(cls.name):
             self.schemaobj.properties[underscore(aliased_slot_name)] = prop
 
-    def xxxvisit_slot(self, slot_name: str, slot: SlotDefinition) -> None:
-        # Don't emit redefined slots unless we are inlining
-        if slot_name == slot.name or self.inline:
-            defn = JsonObj(type="array", items=self.type_or_ref(slot.range)) if slot.multivalued \
-                   else self.type_or_ref(slot.range)
-            if slot.description:
-                defn.description = slot.description
-            self.schemaobj.definitions[underscore(slot.name)] = defn
-
 
 @shared_arguments(JsonSchemaGenerator)
 @click.command()


Test patch:
diff --git a/tests/test_issues/source/issue_129.yaml b/tests/test_issues/source/issue_129.yaml
new file mode 100644
index 00000000..7f115188
--- /dev/null
+++ b/tests/test_issues/source/issue_129.yaml
@@ -0,0 +1,56 @@
+id: http://example.org/sample/types
+
+prefixes:
+  biolinkml: https://w3id.org/biolink/biolinkml/
+
+imports:
+  - biolinkml:types
+
+types:
+  yearCount:
+    base: int
+    uri: xsd:int
+
+classes:
+  c:
+    slots:
+      - id
+      - age in years
+      - scores
+      - has prop
+      - has d
+      - has ds
+      - children
+      - parent
+  d:
+    slots:
+      - id
+
+
+slots:
+
+  id:
+    identifier: true
+
+  scores:
+    range: float
+    multivalued: true
+  has prop:
+    range: boolean
+  age in years:
+    range: yearCount
+  has d:
+    range: d
+    multivalued: false
+    inlined: true
+  has ds:
+    range: d
+    multivalued: true
+    inlined: true
+  children:
+    range: c
+    multivalued: true
+  parent:
+    range: c
+    multivalued: false
+
diff --git a/tests/test_issues/test_issue_129.py b/tests/test_issues/test_issue_129.py
new file mode 100644
index 00000000..f6b2560e
--- /dev/null
+++ b/tests/test_issues/test_issue_129.py
@@ -0,0 +1,47 @@
+import os
+import unittest
+import json
+
+from biolinkml.generators.jsonschemagen import JsonSchemaGenerator
+from biolinkml.generators.owlgen import OwlSchemaGenerator
+from tests.test_issues import sourcedir
+
+
+class IssueJSONSchemaTypesTestCase(unittest.TestCase):
+
+    def header(self, txt: str) -> str:
+        return '\n' + ("=" * 20) + f" {txt} " + ("=" * 20)
+
+    def test_issue_types(self):
+        """ Make sure that types are generated as part of the output """
+        yaml_fname = os.path.join(sourcedir, 'issue_129.yaml')
+        gen = JsonSchemaGenerator(yaml_fname)
+        gen.topCls = 'c'
+        jsonschema = gen.serialize()
+        print(self.header("JSONSchema"))
+        print(jsonschema)
+        sobj = json.loads(jsonschema)
+        defs = sobj['definitions']
+        C = defs['C']
+        props = C['properties']
+        assert props['age_in_years']['type'] == 'integer'
+        assert props['has_prop']['type'] == 'boolean'
+        # multivalued primitive type, inlined
+        assert props['scores']['type'] == 'array'
+        assert props['scores']['items']['type'] == 'number'
+        # single-valued complex type, inlined
+        assert props['has_d']['$ref'] == "#/definitions/D"
+
+        # multi-valued, inlined
+        assert props['has_ds']['type'] == 'array'
+        assert props['has_ds']['items']['$ref'] == "#/definitions/D"
+
+        # single-valued, non-inlined (foreign key)
+        assert props['parent']['type'] == "string"
+
+        # multi-valued, non-inlined (foreign key)
+        assert props['children']['type'] == 'array'
+        assert props['children']['items']['type'] == "string"
+
+if __name__ == '__main__':
+    unittest.main()