CloudObjects / Directory / JSON / SchemaConverter
Sign in

SchemaConverter

a phpmae:Class in JSON
Public PHP Methods

This class has no public methods yet or a problem occurred while retrieving them.

Source Code
<?php

use Exception;
use ML\IRI\IRI;
use ML\JsonLD\JsonLD, ML\JsonLD\Graph, ML\JsonLD\Node;
use Symfony\Component\Yaml\Yaml;
use CloudObjects\SDK\COIDParser;

/**
 * Implementation for coid://json.co-n.net/SchemaConverter
 */
class SchemaConverter {

    const CO = 'coid://cloudobjects.io/';
    const JSON = 'coid://json.co-n.net/';
    const RDFS = 'http://www.w3.org/2000/01/rdf-schema#';

    private $map = [
        // For all:
        'default' => 'hasDefaultValue',
        'format' => 'hasFormat',
        // For numbers:
        'multipleOf' => 'isMultipleOf',
        'maximum' => 'hasMaximum',
        'exclusiveMaximum' => 'isMaximumExclusive',
        'minimum' => 'hasMinimum',
        'exclusiveMinimum' => 'isMinimumExclusive',
        // For strings:
        'maxLength' => 'hasMaxLength',
        'minLength' => 'hasMinLength',
        'pattern' => 'hasPattern',
        // For arrays:
        'maxItems' => 'hasMaxItems',
        'minItems' => 'hasMinItems'
    ];

    private $combineMap = [
        'oneOf' => 'isOneOf',
        'allOf' => 'isAllOf',
        'anyOf' => 'isAnyOf'
    ];

    private function convertElement(array $element, Graph $graph, Node $node,
            string $rewriteRule = null) {

        if (isset($element['$ref'])) {
            // Handle unresolved reference
            $node->setType($graph->createNode(self::JSON.'ElementWithSchema'));
            $target = 'jsonpointer:'.$element['$ref'].'-'.$rewriteRule;
            if (isset($rewriteRule)) {
                $rewriteRule = explode(' ', $rewriteRule);
                $reference = $rewriteRule[1];
                @preg_match('/'.$rewriteRule[0].'/', $element['$ref'], $matches);
                for ($i = 0; $i < count($matches); $i++)
                    $reference = str_replace('$'.$i, $matches[$i], $reference);
                $targetCoid = COIDParser::fromString($reference);
                if (in_array(COIDParser::getType($targetCoid),
                        [ COIDParser::COID_VERSIONED, COIDParser::COID_UNVERSIONED ]))
                    $target = $reference;
            }
            $node->addPropertyValue(self::JSON.'hasSchema', $graph->createNode($target));
            return;
        }

        // Add title -> rdfs:label
        if (isset($element['title']) && !empty(trim($element['title'])))
            $node->setProperty(self::RDFS.'label', $element['title']);
        
        // Add description -> rdfs:comment
        if (isset($element['description']) && !empty(trim($element['description'])))
            $node->setProperty(self::RDFS.'comment', $element['description']);

        $types = isset($element['type'])
            ? (is_array($element['type']) ? $element['type'] : [ $element['type'] ])
            : [];

        // Element without a specified type? -> json:Element
        if (count($types) == 0)            
            $node->setType($graph->createNode(self::JSON.'Element'));

        foreach ($types as $t) {
            switch ($t) {
                case "string":
                case "boolean":
                case "number":
                case "integer":
                case "null":
                    // Simple types
                    $name = strtoupper($t[0]).substr($t, 1);
                    $node->addType($graph->createNode(self::JSON.$name));
                    break;
                case "object":
                    // Object types are handled recursively
                    if (isset($element['properties'])) {
                        foreach ((array)$element['properties'] as $k => $e) {
                            $newNode = $graph->createNode();
                            $newNode->setProperty(self::JSON.'hasKey', $k);
                            if (isset($element['required']) && in_array($k, $element['required']))
                                $newNode->setProperty(self::JSON.'isRequired', 'true');

                            $this->convertElement((array)$e, $graph, $newNode, $rewriteRule);
                            $node->addPropertyValue(self::JSON.'hasProperty', $newNode);
                        }
                    }
                    $node->setType($graph->createNode(self::JSON.'Object'));
                    break;
                case "array":
                    $node->setType($graph->createNode(self::JSON.'Array'));
                    if (isset($element['items'])) {
                        $newNode = $graph->createNode();
                        $this->convertElement((array)$element['items'], $graph,
                            $newNode, $rewriteRule);
                        $node->addPropertyValue(self::JSON.'containsItems', $newNode);
                    }
                    break;
                default:
                    throw new Exception("Unknown value for 'type': ".$t);
            }
        }        
        
        if (isset($element['enum'])) {
            // Add enumeration of valid values
            foreach ((array)$element['enum'] as $enum)
                $node->addPropertyValue(self::JSON.'allowsValue', $enum);
        }
        
        foreach ($this->map as $key => $propName) {
            // Go through additional validations
            if (isset($element[$key]))
                $node->addPropertyValue(self::JSON.$propName, $element[$key]);
        }

        foreach ($this->combineMap as $key => $propName) {
            // Go through combining
            if (isset($element[$key]) && is_array($element[$key])) {
                foreach ($element[$key] as $subschema) {
                    $newNode = $graph->createNode();
                    $subschema = (array)$subschema;
                    if (isset($element['type']) && !isset($subschema['type']))
                        $subschema['type'] = $element['type'];
                    $this->convertElement((array)$subschema, $graph,
                        $newNode, $rewriteRule);
                    $node->addPropertyValue(self::JSON.$propName, $newNode);
                }                
            }
        }   
    }

    /**
     * Converts a JSON schema into a configuration job.
     * 
     * @param string $data Content of the JSON schema.
     * @param string $coid The desired COID for the schema.
     * @param string $rewrite_rule A rule to rewrite references into COIDs.
     * @param string $audience_coid The desired audience for the API.
     */
    public function convert(string $data, string $coid, string $rewrite_rule,
            string $audience_coid) {
        try {
            $data = Yaml::parse($data);

            $coidAsIri = COIDParser::fromString($coid);
            if (!in_array(COIDParser::getType($coidAsIri),
                    [ COIDParser::COID_VERSIONED, COIDParser::COID_UNVERSIONED ]))
                throw new Exception("Invalid COID for schema!");

            $graph = JsonLD::getDocument('{}')->getGraph();
            $schema = $graph->createNode((string)$coidAsIri);

            // Add audience if provided
            if (isset($audience_coid) && !empty($audience_coid)) {
                $audienceAsIri = COIDParser::fromString($audience_coid);
                if (!in_array(COIDParser::getType($audienceAsIri),
                        [ COIDParser::COID_VERSIONED, COIDParser::COID_UNVERSIONED ]))
                    throw new Exception("Invalid COID for audience parameter!");
            
                $audienceNode = $graph->createNode($audience_coid);
                $schema->setProperty(self::CO.'isVisibleTo', $audienceNode);
                $schema->setProperty(self::CO.'permitsUsageTo', $audienceNode);
            }

            $this->convertElement($data, $graph, $schema, $rewrite_rule);

            // Return compact JSON-LD response
            $context = (object)[ 'json' => self::JSON, 'rdfs' => self::RDFS ];
            return [
                'success' => true,
                'configuration_job' => JsonLD::compact($graph->toJsonLd(), $context)
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'error_message' => $e->getMessage()
            ];
        }        
    }

    /**
     * Parses JSON schema data and returns a graph node.
     * @param array $data JSON Schema
     * @param string $id The optional COID, URI, or BNode ID for the schema
     * @param Graph $graph Adds the schema to this graph
     * @param string|null $rewrite_rule A rule to rewrite references into COIDs.
     * 
     * @return Node
     */
    public function convertToNode(array $data, string $id = null, Graph $graph,
            string $rewrite_rule = null) {

        if (isset($id) && $graph->getNode($id) != null)
            return $graph->getNode($id); // do not create if already exists in graph

        $node = isset($id) ? $graph->createNode($id, true) : $graph->createNode();
        $this->convertElement($data, $graph, $node, $rewrite_rule);

        if (count($node->getProperties()) == 2 && $node->getType()->getId()
                == 'coid://json.co-n.net/ElementWithSchema') {
            // Remove an unnecessary layer of redirection
            $targetNode = $node->getProperty('coid://json.co-n.net/hasSchema');
            $graph->removeNode($node);
            return $targetNode;
        }

        return $node;
    }

}
Meta
URI / COID
coid://json.co-n.net/SchemaConverter content_copy
Revision
4-b03d77fdbb0adbdc502cc6f84f34f587 content_copy
Short ID
json:SchemaConverter content_copy
Reference URL
https://coid.link/json.co-n.net/SchemaConverter content_copy
Last updated
2021-10-02 14:29 (UTC)
Created at
2019-12-13 16:30 (UTC)