irJSONParser.php
Go to the documentation of this file.
00001 <?php 00002 00005 00006 00027 class irJSONParser 00028 { 00030 public $instanceRecords = array(); 00031 00033 public $linkageSchemas = array(); 00034 00036 public $structureSchemas = array(); 00037 00039 public $dataset; 00040 00042 public $jsonErrors = array(); 00043 00045 public $irjsonErrors = array(); 00046 00048 public $irjsonNotices = array(); 00049 00051 private $jsonContent = ""; 00052 00054 private $irvStructureSchema = 00055 '{ 00056 "schema": { 00057 "version": "0.1", 00058 00059 "attributeList": { 00060 00061 "id": { 00062 "allowedValue": "String", 00063 00064 "maxValues": "1" 00065 }, 00066 "type": { 00067 "allowedValue": "Object" 00068 }, 00069 "ref": { 00070 "allowedValue": "String", 00071 00072 "maxValues": "1" 00073 }, 00074 00075 00076 "prefLabel": { 00077 "allowedValue": "String", 00078 00079 "maxValues": "1" 00080 }, 00081 "altLabel": { 00082 "allowedValue": "String", 00083 00084 "maxValues": "1" 00085 }, 00086 "description": { 00087 "allowedValue": "String", 00088 00089 "maxValues": "1" 00090 }, 00091 "prefURL": { 00092 "allowedValue": "String", 00093 00094 "format": "url" 00095 }, 00096 00097 "createDate": { 00098 "allowedValue": "String", 00099 00100 "format": "date" 00101 }, 00102 "source": { 00103 "allowedValue": "Object" 00104 }, 00105 "creator": { 00106 "allowedValue": "Object" 00107 }, 00108 "curator": { 00109 "allowedValue": "Object" 00110 }, 00111 "maintainer": { 00112 "allowedValue": "Object" 00113 }, 00114 00115 "linkage": { 00116 "allowedValue": "String" 00117 }, 00118 "schema": { 00119 "allowedValue": "String" 00120 }, 00121 00122 "allowedValue": { 00123 "allowedValue": "String" 00124 }, 00125 "allowedType": { 00126 "allowedValue": "String" 00127 }, 00128 "minValues": { 00129 "allowedValue": "String" 00130 }, 00131 "maxValues": { 00132 "allowedValue": "String" 00133 }, 00134 "requiredAttribute": { 00135 "allowedValue": "String" 00136 }, 00137 "orderedValues": { 00138 "allowedValue": "String" 00139 }, 00140 00141 00142 "version": { 00143 "allowedValue": "String", 00144 00145 "maxValues": "1" 00146 }, 00147 "linkedType": { 00148 "allowedValue": "String", 00149 00150 "maxValues": "1" 00151 }, 00152 "prefixList": { 00153 "allowedValue": "Object" 00154 }, 00155 "attributeList": { 00156 "allowedValue": "Object" 00157 }, 00158 "typeList": { 00159 "allowedValue": "Object" 00160 }, 00161 "format": { 00162 "allowedValue": "String" 00163 }, 00164 "mapTo": { 00165 "allowedValue": "String" 00166 }, 00167 "addMapping": { 00168 "allowedValue": "Object" 00169 }, 00170 "subPropertyOf": { 00171 "allowedValue": "Object" 00172 }, 00173 "equivalentPropertyTo": { 00174 "allowedValue": "Object" 00175 }, 00176 "subTypeOf": { 00177 "allowedValue": "Object" 00178 }, 00179 "equivalentTypeTo": { 00180 "allowedValue": "Object" 00181 } 00182 } 00183 } 00184 }'; 00185 00196 function __construct($content) 00197 { 00198 $this->jsonContent = json_decode($content); 00199 00200 if(strnatcmp(phpversion(), '5.3.0') >= 0) 00201 { 00202 // Additional parsing errors. Only for PHP >= 5.3 00203 00204 $error = json_last_error(); 00205 00206 if($error != JSON_ERROR_NONE) 00207 { 00208 switch($error) 00209 { 00210 case JSON_ERROR_DEPTH: 00211 array_push($this->jsonErrors, "Maximum stack depth exceeded. Can't parse: '$content'"); 00212 break; 00213 00214 case JSON_ERROR_CTRL_CHAR: 00215 array_push($this->jsonErrors, "Unexpected control character found. Can't parse: '$content'"); 00216 break; 00217 00218 case JSON_ERROR_SYNTAX: 00219 array_push($this->jsonErrors, "Syntax error, malformed JSON. Can't parse: '$content'"); 00220 break; 00221 } 00222 00223 return FALSE; 00224 } 00225 } 00226 00227 if($this->jsonContent === NULL) 00228 { 00229 array_push($this->jsonErrors, "Syntax error, malformed JSON. Cant parse: '$content'"); 00230 00231 return FALSE; 00232 } 00233 else 00234 { 00235 $this->parse(); 00236 } 00237 } 00238 00239 function __destruct() { } 00240 00249 private function parse() 00250 { 00251 // Populate the Dataset object 00252 $this->dataset = new Dataset(); 00253 00254 // Set ID 00255 if(isset($this->jsonContent->dataset->id)) 00256 { 00257 $this->dataset->setId($this->jsonContent->dataset->id); 00258 } 00259 else 00260 { 00261 array_push($this->irjsonErrors, "Dataset ID not specified"); 00262 } 00263 00264 // Set attributes 00265 foreach($this->jsonContent->dataset as $attribute => $value) 00266 { 00267 if($attribute != "id" && $attribute != "type" && $attribute != "linkage" && $attribute != "schema") 00268 { 00269 // Check if we have an array of something 00270 if(is_array($this->jsonContent->dataset->{$attribute})) 00271 { 00272 foreach($this->jsonContent->dataset->{$attribute} as $arrayValue) 00273 { 00274 if(gettype($arrayValue) == "string") 00275 { 00276 $this->dataset->setAttribute($attribute, $arrayValue, 00277 "primitive:string[" . count($this->jsonContent->dataset->{$attribute}) . "]"); 00278 } 00279 00280 if(gettype($arrayValue) == "object") 00281 { 00282 // Create the metaData array 00283 $metaData = array(); 00284 00285 foreach($arrayValue as $metaAttribute => $metaValue) 00286 { 00287 if($metaAttribute != "ref") 00288 { 00289 array_push($metaData, array( $metaAttribute => $metaValue )); 00290 } 00291 } 00292 00293 $this->dataset->setAttributeRef($attribute, $metaData, $arrayValue->ref, 00294 "type:object[" . $this->jsonContent->dataset->{$attribute}. "]"); 00295 } 00296 } 00297 } 00298 else 00299 { 00300 if(gettype($value) == "string") 00301 { 00302 $this->dataset->setAttribute($attribute, $value, "primitive:string[1]"); 00303 } 00304 00305 if(gettype($value) == "object") 00306 { 00307 // Create the metaData array 00308 $metaData = array(); 00309 00310 foreach($value as $metaAttribute => $metaValue) 00311 { 00312 if($metaAttribute != "ref") 00313 { 00314 array_push($metaData, array( $metaAttribute => $metaValue )); 00315 } 00316 } 00317 00318 $this->dataset->setAttributeRef($attribute, $metaData, $value->ref, "type:object[1]"); 00319 } 00320 } 00321 } 00322 } 00323 00324 00325 /* 00326 // Structured Schema 00327 00328 // Load the internal structure schema in the schemas array. 00329 $structureSchemas = array(); 00330 00331 $parsedContent = json_decode($this->irvStructureSchema); 00332 00333 if($parsedContent === NULL) 00334 { 00335 array_push($this->jsonErrors, "Syntax error while parsing core structure schema, malformed JSON"); 00336 00337 return FALSE; 00338 } 00339 00340 array_push($structureSchemas, $parsedContent); 00341 00342 00343 if(isset($this->jsonContent->dataset->structureSchema)) 00344 { 00345 array_push($structureSchemas, $this->jsonContent->dataset->structureSchema); 00346 } 00347 00348 // Get the decoded schema 00349 if(gettype($this->jsonContent->dataset->structureSchema) != "object") 00350 { 00351 // It is a URL link to a linkage schema 00352 if(isset($this->jsonContent->dataset->structureSchema)) 00353 { 00354 // Save the URL reference 00355 $this->dataset->setStructureSchema($this->jsonContent->dataset->structureSchema); 00356 00357 $ch = curl_init(); 00358 00359 curl_setopt($ch, CURLOPT_URL, $this->jsonContent->dataset->structureSchema); 00360 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 00361 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 00362 00363 00364 $data = curl_exec($ch); 00365 00366 if(curl_errno($ch)) 00367 { 00368 array_push($this->irjsonNotices, "Cannot access the structure schema from: ".$this->jsonContent->dataset->structureSchema." - Ignored"); 00369 00370 curl_close($ch); 00371 } 00372 else 00373 { 00374 $data = trim($data); 00375 00376 curl_close($ch); 00377 00378 $parsedContent = json_decode($data); 00379 00380 if($parsedContent === NULL) 00381 { 00382 array_push($this->jsonErrors, "Syntax error while parsing structure schema '".$this->jsonContent->dataset->structureSchema."', malformed JSON"); 00383 00384 return FALSE; 00385 } 00386 00387 array_push($structureSchemas, $parsedContent); 00388 } 00389 } 00390 } 00391 else 00392 { 00393 if(isset($this->jsonContent->dataset->structureSchema)) 00394 { 00395 array_push($structureSchemas, $this->jsonContent->dataset->structureSchema); 00396 } 00397 } 00398 00399 // Now populate the schema object. 00400 foreach($structureSchemas as $structureSchema) 00401 { 00402 $structureSchema = $structureSchema->structureSchema; 00403 $tempSchema = new StructureSchema(); 00404 00405 // Set version 00406 $tempSchema->setVersion($structureSchema->version); 00407 00408 // Set properties structureSchemas 00409 if(isset($structureSchema->properties)) 00410 { 00411 foreach($structureSchema->properties as $property => $values) 00412 { 00413 $tempSchema->setPropertyX($property, $values->type, $values->format, $values->equivalentPropertyTo, $values->subPropertyOf); 00414 } 00415 } 00416 00417 // Set types 00418 if(isset($structureSchema->types)) 00419 { 00420 foreach($structureSchema->types as $type => $values) 00421 { 00422 $tempSchema->setTypeX($type, $values->mapTo, $values->equivalentTypeTo, $values->subTypeOf); 00423 } 00424 } 00425 00426 array_push($this->structureSchemas, $tempSchema); 00427 } 00428 */ 00429 00430 // Linkage Schemas 00431 $linkageSchemas = array(); 00432 00433 // Get the decoded schema 00434 if(gettype($this->jsonContent->dataset->linkage) != "object") 00435 { 00436 // It is a URL link to a linkage schema 00437 if(isset($this->jsonContent->dataset->linkage)) 00438 { 00439 foreach($this->jsonContent->dataset->linkage as $ls) 00440 { 00441 $data = ""; 00442 00443 if(gettype($ls) != "object") 00444 { 00445 // It is a URL 00446 00447 // Save the URL reference 00448 $this->dataset->setLinkageSchema($ls); 00449 00450 $ch = curl_init(); 00451 00452 curl_setopt($ch, CURLOPT_URL, $ls); 00453 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 00454 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 00455 00456 $data = curl_exec($ch); 00457 00458 if(curl_errno($ch) || $data == "") 00459 { 00460 array_push($this->irjsonNotices, "Cannot access the linkage schema from: $ls - Ignored"); 00461 curl_close($ch); 00462 continue; 00463 } 00464 00465 $data = trim($data); 00466 00467 curl_close($ch); 00468 } 00469 else 00470 { 00471 // It is an embedded linkage schema. 00472 array_push($linkageSchemas, $ls); 00473 } 00474 00475 if($data != "") 00476 { 00477 $parsedContent = json_decode($data); 00478 00479 if($parsedContent === NULL) 00480 { 00481 array_push($this->jsonErrors, 00482 "Syntax error while parsing core linkage schema '" . $ls . "', malformed JSON"); 00483 00484 return FALSE; 00485 } 00486 00487 // Check if a linkage schema of the same linkageType exists. If it exists, we merge them together. 00488 00489 // Merging rules: 00490 // (1) if a type already exists, the type of the first schema will be used 00491 // (2) if an attribute already exists, the attribute of the first schema will be used. 00492 00493 $parsedContent = $parsedContent->linkage; 00494 00495 $merged = FALSE; 00496 00497 foreach($linkageSchemas as $linkageSchema) 00498 { 00499 if($linkageSchema->linkageType == $parsedContent->linkageType) 00500 { 00501 // merge prefixes 00502 if(isset($parsedContent->prefixList)) 00503 { 00504 foreach($parsedContent->prefixList as $prefix => $uri) 00505 { 00506 if(!isset($linkageSchema->prefixList->{$prefix})) 00507 { 00508 $linkageSchema->prefixList->{$prefix}= $uri; 00509 } 00510 } 00511 } 00512 00513 // merge types 00514 if(isset($parsedContent->typeList)) 00515 { 00516 foreach($parsedContent->typeList as $type => $typeObject) 00517 { 00518 if(!isset($linkageSchema->typeList->{$type})) 00519 { 00520 $linkageSchema->typeList->{$type}= $typeObject; 00521 } 00522 } 00523 } 00524 00525 // merge attributes 00526 if(isset($parsedContent->attributeList)) 00527 { 00528 foreach($parsedContent->attributeList as $attribute => $attributeObject) 00529 { 00530 if(!isset($linkageSchema->attributeList->{$attribute})) 00531 { 00532 $linkageSchema->attributeList->{$attribute}= $attributeObject; 00533 } 00534 } 00535 } 00536 } 00537 00538 $merged = TRUE; 00539 } 00540 00541 if(!$merged) 00542 { 00543 array_push($linkageSchemas, $parsedContent); 00544 } 00545 } 00546 } 00547 } 00548 } 00549 else 00550 { 00551 if(isset($this->jsonContent->dataset->linkage)) 00552 { 00553 array_push($linkageSchemas, $this->jsonContent->dataset->linkage); 00554 } 00555 } 00556 00557 // Now populate the schema object. 00558 foreach($linkageSchemas as $linkageSchema) 00559 { 00560 $tempSchema = new LinkageSchema(); 00561 00562 // Set version 00563 $tempSchema->setVersion($linkageSchema->version); 00564 00565 // Set linkedType 00566 $tempSchema->setLinkedType($linkageSchema->linkedType); 00567 00568 // Set prefixes 00569 if(isset($linkageSchema->prefixList)) 00570 { 00571 foreach($linkageSchema->prefixList as $prefix => $uri) 00572 { 00573 $tempSchema->setPrefix($prefix, $uri); 00574 } 00575 } 00576 00577 // Set attributes 00578 if(isset($linkageSchema->attributeList)) 00579 { 00580 foreach($linkageSchema->attributeList as $property => $values) 00581 { 00582 // Throw an error if mapTo is used without specifying a linkedType attribute. 00583 if(!isset($linkageSchema->linkedType) && isset($values->mapTo)) 00584 { 00585 array_push($this->irjsonErrors, 00586 "A 'linkedType' attribute has to be defined for this schema since the 'mapTo' attribute is used in the schema."); 00587 } 00588 00589 $error = ""; 00590 00591 $tempSchema->setPropertyX($property, $values->mapTo, $error); 00592 00593 if($error != "") 00594 { 00595 array_push($this->irjsonErrors, $error); 00596 } 00597 } 00598 } 00599 00600 // Set types 00601 if(isset($linkageSchema->typeList)) 00602 { 00603 foreach($linkageSchema->typeList as $type => $values) 00604 { 00605 $adds = array(); 00606 00607 if(isset($values->add)) 00608 { 00609 foreach($values->add as $key => $value) 00610 { 00611 $adds[$key] = $value; 00612 } 00613 } 00614 00615 $error = ""; 00616 00617 $tempSchema->setTypeX($type, $values->mapTo, $adds, $error); 00618 00619 if($error != "") 00620 { 00621 array_push($this->irjsonErrors, $error); 00622 } 00623 } 00624 } 00625 00626 array_push($this->linkageSchemas, $tempSchema); 00627 } 00628 00629 $this->instanceRecords = array(); 00630 00631 foreach($this->jsonContent->recordList as $ir) 00632 { 00633 $instanceRecord = new InstanceRecord(); 00634 00635 // Set ID 00636 if(isset($ir->id)) 00637 { 00638 $instanceRecord->setId($ir->id); 00639 } 00640 else 00641 { 00642 // Generate a random ID for that instance record 00643 $instanceRecord->setId(md5(microtime())); 00644 } 00645 00646 // Set default type in case that no type has been defined for this record 00647 if(isset($ir->type)) 00648 { 00649 if(is_array($ir->type)) 00650 { 00651 foreach($ir->type as $type) 00652 { 00653 $instanceRecord->setAttribute("type", $type, "type:object[" . count($ir->type) . "]"); 00654 } 00655 } 00656 else 00657 { 00658 $instanceRecord->setAttribute("type", $ir->type, "type:object[1]"); 00659 } 00660 } 00661 else 00662 { 00663 $instanceRecord->setAttribute("type", "Object", "type:object[1]"); 00664 } 00665 00666 // Set attributes 00667 foreach($ir as $attribute => $value) 00668 { 00669 if($attribute != "id" && $attribute != "type") 00670 { 00671 // Check if we have an array of something 00672 if(is_array($ir->{$attribute})) 00673 { 00674 foreach($ir->{$attribute} as $arrayValue) 00675 { 00676 if(gettype($arrayValue) == "string") 00677 { 00678 $instanceRecord->setAttribute($attribute, $arrayValue, 00679 "primitive:string[" . count($ir->{$attribute}) . "]"); 00680 } 00681 00682 if(gettype($arrayValue) == "object") 00683 { 00684 // Create the metaData array 00685 $metaData = array(); 00686 00687 foreach($arrayValue as $metaAttribute => $metaValue) 00688 { 00689 if($metaAttribute != "ref") 00690 { 00691 array_push($metaData, array( $metaAttribute => $metaValue )); 00692 } 00693 } 00694 $instanceRecord->setAttributeRef($attribute, $metaData, $arrayValue->ref, 00695 "type:object[" . $ir->{$attribute}. "]"); 00696 } 00697 } 00698 } 00699 else 00700 { 00701 if(gettype($value) == "string") 00702 { 00703 $instanceRecord->setAttribute($attribute, $value, "primitive:string[1]"); 00704 } 00705 00706 if(gettype($value) == "object") 00707 { 00708 $metaData = array(); 00709 00710 foreach($value as $metaAttribute => $metaValue) 00711 { 00712 if($metaAttribute != "ref") 00713 { 00714 array_push($metaData, array( $metaAttribute => $metaValue )); 00715 } 00716 } 00717 00718 $instanceRecord->setAttributeRef($attribute, $metaData, $value->ref, "type:object[1]"); 00719 } 00720 } 00721 } 00722 } 00723 00724 array_push($this->instanceRecords, $instanceRecord); 00725 } 00726 00727 00728 /* 00729 00730 // Now lets validate the types of the values of the attributes that have been parsed. 00731 00732 // Dataset types. 00733 00734 foreach($this->dataset as $property => $value) 00735 { 00736 if($this->dataset->getValueType($property) === FALSE) 00737 { 00738 // Property not defined. 00739 continue; 00740 } 00741 00742 $possibleTypes = array(); 00743 $defined = FALSE; 00744 00745 foreach($this->structureSchemas as $structureSchema) 00746 { 00747 $validType = FALSE; 00748 00749 if($structureSchema->getPropertyTypes($property) !== FALSE) 00750 { 00751 $defined = TRUE; 00752 foreach($structureSchema->getPropertyTypes($property) as $type) 00753 { 00754 // array(object) and object; and; array(string) and string are the same types 00755 $t = $type; 00756 00757 if(strpos($t, "array") !== FALSE) 00758 { 00759 $t = substr($t, 6, strlen($t) - 7); 00760 } 00761 00762 $prop = $this->dataset->getValueType($property); 00763 00764 if(strpos($prop, "array") !== FALSE) 00765 { 00766 $prop = substr($prop, 6, strlen($prop) - 7); 00767 } 00768 00769 if($t == $prop) 00770 { 00771 // The type of the value has been validated by one of the structure schema. 00772 $validType = TRUE; 00773 break; 00774 } 00775 00776 array_push($possibleTypes, $type); 00777 } 00778 } 00779 00780 if($validType){ break; } 00781 } 00782 00783 if($validType === FALSE && $defined === TRUE) 00784 { 00785 // The type of the value is not valid according to the structure schemas. 00786 array_push($this->irjsonErrors, "Dataset property '".$property."' with value type '".$this->dataset->getValueType($property)."' is not valid according to the definition of the structure schema (should be one of: ".$this->listTypes($possibleTypes)." )"); 00787 } 00788 00789 if($defined === FALSE) 00790 { 00791 // The type of the value is not valid according to the structure schemas. 00792 array_push($this->irjsonNotices, "Dataset property '".$property."' used without being part of the core structure schema.)"); 00793 } 00794 } 00795 00796 // Instance Record types 00797 00798 foreach($this->instanceRecords as $key => $instanceRecord) 00799 { 00800 foreach($instanceRecord as $attribute => $value) 00801 { 00802 if($attribute == "attributeX") 00803 { 00804 foreach($value as $attr => $val) 00805 { 00806 if($instanceRecord->getValueType($attr) === FALSE) 00807 { 00808 // Property not defined. 00809 continue; 00810 } 00811 00812 $this->validateAttributeType($instanceRecord, $attr); 00813 } 00814 } 00815 else 00816 { 00817 if($instanceRecord->getValueType($attribute) === FALSE) 00818 { 00819 // Property not defined. 00820 continue; 00821 } 00822 00823 $this->validateAttributeType($instanceRecord, $attribute); 00824 } 00825 } 00826 } 00827 */ 00828 } 00829 00841 private function validateAttributeType(&$instanceRecord, $attribute) 00842 { 00843 $possibleTypes = array(); 00844 $defined = FALSE; 00845 00846 foreach($this->structureSchemas as $structureSchema) 00847 { 00848 $validType = FALSE; 00849 00850 if($structureSchema->getPropertyTypes($attribute) !== FALSE) 00851 { 00852 $defined = TRUE; 00853 00854 foreach($structureSchema->getPropertyTypes($attribute) as $type) 00855 { 00856 // array(object) and object; and; array(string) and string are the same types 00857 $t = $type; 00858 00859 if(strpos($t, "array") !== FALSE) 00860 { 00861 $t = substr($t, 6, strlen($t) - 7); 00862 } 00863 00864 $prop = $instanceRecord->getValueType($attribute); 00865 00866 if(strpos($prop, "array") !== FALSE) 00867 { 00868 $prop = substr($prop, 6, strlen($prop) - 7); 00869 } 00870 00871 if($t == $prop) 00872 { 00873 // The type of the value has been validted by one of the structure schema. 00874 $validType = TRUE; 00875 break; 00876 } 00877 00878 array_push($possibleTypes, $type); 00879 } 00880 } 00881 00882 if($validType) 00883 { 00884 break; 00885 } 00886 } 00887 00888 if($validType === FALSE && $defined === TRUE) 00889 { 00890 // The type of the value is not valid according to the structure schemas. 00891 array_push($this->irjsonErrors, 00892 "Instance record attribute '" . $attribute . "' with value type '" . $instanceRecord->getValueType($attribute) 00893 . "' is not valid according to the definition of the structure schema (should be one of: " 00894 . $this->listTypes($possibleTypes) . " )"); 00895 } 00896 00897 if($defined === FALSE) 00898 { 00899 // The type of the value is not valid according to the structure schemas. 00900 array_push($this->irjsonNotices, 00901 "Instance record attribute '" . $attribute . "' used without being part of the structure schema.)"); 00902 } 00903 00904 return ($validType); 00905 } 00906 00917 private function listTypes($types) 00918 { 00919 $typeStr = ""; 00920 00921 foreach($types as $type) 00922 { 00923 $typeStr .= $type . ", "; 00924 } 00925 00926 return (substr($typeStr, 0, strlen($typeStr) - 2)); 00927 } 00928 } 00929 00930 00932 00933 ?>
