swaggest / php-json-schema

High definition PHP structures with JSON-schema based validation
MIT License
438 stars 50 forks source link

$ref and other properties in JSON Reference #136

Open massadm opened 2 years ago

massadm commented 2 years ago
namespace Swaggest\JsonSchema;
interface RemoteRefProvider
{
    public function getSchemaData($url);
}

Is it possible in getSchemaData to get an JSON object, which contains a member named $ref?

[...]
{
"title": "my_value",
"description": "my_value",
"$ref": "..."
}
[...]

I am aware of the limitations of a JSON Reference, but I need to do it in such a way that the properties with the value "my_value" from the example override the own properties of the schema referenced by $ref. To do this, in getSchemaData I need access to the object containing the $ref, the rest is trivial.

Any solution that works will do!

vearutop commented 2 years ago

@massadm, sorry for late response.

Please check example implementation in https://github.com/swaggest/php-json-schema/commit/22eadfeb4a31bfc61073b7c2102b212e07266d22. I hope I understood your task correctly.

massadm commented 2 years ago

Thanks for the suggested solution. This solution is simple and effective, but not applicable in my case.

If I modify your example like this:

...
    public function testPatchSchema()
    {
        $refProvider = new Preloaded();
        $refProvider->setSchemaData(
            'https://example.com/unixtimestamp-type.json',
            json_decode(<<<'JSON'
{
    "title": "UNIX Timestamp",
    "description": "Number of seconds that have elapsed since the Unix epoch",
    "type": "integer"
}
JSON
            )
        );

        $schemaData = json_decode(<<<'JSON'
{
    "properties": {
        "atime": {
            "title": "Access timestamp",
            "description": "May not update if has been accessed recently",
            "$ref":"https://example.com/unixtimestamp-type.json"
        },
        "mtime": {
            "title": "Modification timestamp",
            "$ref":"https://example.com/unixtimestamp-type.json"
        },
        "ctime": {
            "title": "Change timestamp",
            "$ref":"https://example.com/unixtimestamp-type.json"
        }
    }
}
JSON
        );
...

After:

...
$schema = Schema::import(...);
echo json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

I would like the result to be:

{
...
    "properties": {
        "atime": {
            "title": "Access timestamp",
            "description": "May not update if has been accessed recently",
            "type": "integer"
        },
        "mtime": {
            "title": "Modification timestamp",
            "description": "Number of seconds that have elapsed since the Unix epoch",
            "type": "integer"
        },
        "ctime": {
            "title": "Change timestamp",
            "description": "Number of seconds that have elapsed since the Unix epoch",
            "type": "integer"
        }
    }
}

(titles and descriptions have merged)

So in getSchemaData() I need not only resource content but also entire object containing the $ref:

{
   "title": "Access timestamp",
   "description": "May not update if has been accessed recently",
   "$ref":"https://example.com/unixtimestamp-type.json"
}

That way I can get the fields to be merged.

I am aware of the limitations of a JSON Reference: when an object contains a $ref property, the object is considered a reference, not a schema. For me additional properties to the $ref is something like context for JSON-Pointer.

My case is specific of course. Now I use such constructions, but they are very cumbersome:

...
    "properties": {
        "atime": {
            "title": "Access timestamp",
            "description": "May not update if has been accessed recently",
            "allOf": [{ "$ref": "https://example.com/unixtimestamp-type.json" }]
        },
...
vearutop commented 2 years ago

Latest version of JSON schema spec allows keywords adjacent to $ref (originally defined in https://github.com/json-schema-org/json-schema-spec/pull/628), unfortunately this library is stuck at draft-07 because I haven't had practical need for newer spec versions. I was planning to implement support for newer drafts, hopefully I will find time for that.

Using allOf to nest reference(s) was an idiomatic approach in draft-07.

Could you tell a bit more about your use case? What would you do with an instance of schema that has referenced value and titles/descriptions merged at top level?