question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

$recursiveRef: only supports hash fragment reference

See original GitHub issue

AJV: 8.6.2 Draft: 2019-09

Hi, I’ve run into into a issue with $recursiveAnchor and $recursiveRef where I’m unable to target non-root schemas using $recursiveRef. For simplicity, below is the TypeScript data structure I’m trying to represent in JSON schema.

interface Element {
    elementId: string
    elements:  Element[]
}

interface Node {
    nodeId:   string
    nodes:    Node[]
    element:  Element
}

The following is the JSON schema I’m using to represent the above type. I believe JSON schema interprets the # pointer in the following to refer to the root schema in nested schemas, causing the inner Element array to be interpreted as an array of type Node. This is expected behaviour.

{
  "additionalProperties": false,
  "$id": "Node",                       // <------------+
  "$recursiveAnchor": true,            //              |
  "type": "object",                    //              |
  "properties": {                      //              |
    "nodeId": {                        //              |
      "type": "string"                 //              |
    },                                 //              |
    "nodes": {                         //              |
      "type": "array",                 //              | { $recursiveRef: '#' } targets root schema "Node"
      "items": {                       //              | 
        "$recursiveRef": "#"           // -------------+ 
      }                                //              | 
    },                                 //              |
    "element": {                       //              |
      "additionalProperties": false,   //              |
      "$id": "Element",                //              |
      "$recursiveAnchor": true,        //              |
      "type": "object",                //              |
      "properties": {                  //              |
        "elementId": {                 //              |
          "type": "string"             //              |
        },                             //              |
        "elements": {                  //              |
          "type": "array",             //              |
          "items": {                   //              |
            "$recursiveRef": "#"       // -------------+
          }
        }
      }
    }
  }
}

To address this, I’ve tried passing an explicit JSON pointer to try and target the appropriate sub schema.

{
  "additionalProperties": false,
  "$id": "Node",                        // <------------+
  "$recursiveAnchor": true,             //              |
  "type": "object",                     //              | Array of Node
  "properties": {                       //              |
    "nodeId": {                         //              |
      "type": "string"                  //              |
    },                                  //              |
    "nodes": {                          //              |
      "type": "array",                  //              |
      "items": {                        //              |
        "$recursiveRef": "Node#"        // -------------+  
      }                              
    },                               
    "element": {                     
      "additionalProperties": false, 
      "$id": "Element",                 // <------------+
      "$recursiveAnchor": true,         //              |
      "type": "object",                 //              |  Array of Element
      "properties": {                   //              |
        "elementId": {                  //              |
          "type": "string"              //              |
        },                              //              |
        "elements": {                   //              |
          "type": "array",              //              |
          "items": {                    //              |
            "$recursiveRef": "Element#" // -------------+
          }
        }
      }
    }
  }
}

However, AJV is reporting the following for the second schema.

Error: "$recursiveRef" only supports hash fragment reference

Not sure if this is against specification, or a possible constraint with some of the architectural changes in AJV to support these newer drafts. I’ve come across https://github.com/ajv-validator/ajv/issues/1198 which suggests dynamic ref scoping (which I’m guessing relates to this) So not sure if this is working as intended or is a bug. However, I’ve tested the second schema in JSON.NET Schema which seems to support referencing of this kind. So not sure.

What do you think is the correct solution to problem?

Allow $recursiveRef to support $id referencing.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
epoberezkincommented, Jul 21, 2021

Yes, it’s a current limitation that requires a bit of thinking to overcome…

0reactions
sinclairzx81commented, Apr 24, 2022

@epoberezkin Hi, was just looking into this issue again, and seem to be having some success using the 2020-12 draft, replacing $recursiveAnchor and $recursiveRef for $dynamicAnchor and $dynamicRef respectively. The previous JSON test case written for this issue appears to be working great.

import Ajv from 'ajv/dist/2020'

const ajv = new Ajv({})

const Node = {
    'additionalProperties': false,
    '$id': 'Node',                            // <------------+
    '$dynamicAnchor': 'node',                 //              |
    'type': 'object',                         //              | Array of Node
    'properties': {                           //              |
        'nodeId': {                           //              |
            'type': 'string'                  //              |
        },                                    //              |
        'nodes': {                            //              |
            'type': 'array',                  //              |
            'items': {                        //              |
                '$dynamicRef': '#node'        // -------------+  
            }
        },
        'element': {
            'additionalProperties': false,
            '$id': 'Element',                     // <------------+
            '$dynamicAnchor': 'element',          //              |
            'type': 'object',                     //              |  Array of Element
            'properties': {                       //              |
                'elementId': {                    //              |
                    'type': 'string'              //              |
                },                                //              |
                'elements': {                     //              |
                    'type': 'array',              //              |
                    'items': {                    //              |
                        '$dynamicRef': '#element' // -------------+
                    }
                }
            }
        }
    }
}

const ok = ajv.validate(Node, {
    'nodeId': '1',
    'nodes': [
        { 'nodeId': '2', 'nodes': [] },
        { 'nodeId': '3', 'nodes': [] },
        { 'nodeId': '4', 'nodes': [] },
        { 'nodeId': '5', 'nodes': [] }
    ],
    'element': {
        'elementId': '3',
        'elements': [
            { 'elementId': '4', 'elements': [] },
            { 'elementId': '5', 'elements': [] }
        ]
    }
}) // -> ok

console.log('ok', ok)

This issue was originally raised as AJV was stack overflowing in the $recursive case (possibly tied to the AJV’s compilation logic) but seems to be fine for the $dynamic implementation on the 2020 draft. So was curious if there had been updates to AJV over the past 10 months to resolve the stack overflow issue.

Would be happy to close off this issue, but wanted to check before swapping over to the 2020 spec.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Combining schemas
$recursiveRef / $dynamicRef can only be hash fragments, without URI. Ajv also does not support dynamic references in asynchronous schemas (Ajv extension) ...
Read more >
Structuring a complex schema
The value of $id is a URI-reference without a fragment that resolves against the Retrieval URI. The resulting URI is the base URI...
Read more >
draft-handrews-json-schema-02
It is inappropriate to use Internet-Drafts as reference material or to cite them ... The "application/schema-instance+json" media type supports one fragment ...
Read more >
Why is the hash part of the URL not available on the server ...
When a URI reference is used to perform a retrieval action on the ... Fragment is the only part of URL free for...
Read more >
袋帯 リバーシブル
API Reference | Ajv JSON schema validator. recursiveRef: only supports hash fragment reference · Issue #1709. Json Schema and Json Validation - Anil ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found