Network Working Group P. Cordell Internet-Draft Codalogic Intended status: Standards Track A. Newton Expires: September 22, 2016 ARIN March 21, 2016 Co-Constraints for JSON Content Rules draft-cordell-jcr-co-constraints-00 Abstract JSON Content Rules (JCR) [JCR] provides a powerful, intuitive and concise method for defining the structure of JSON [RFC7159] messages. However, modern JSON usage patterns occasionally mean that JCR alone is not able to capture the required constraints in a satisfactory way. This document describes JCR Co-Constraints (JCRCC) which defines additional JCR directives and annotations that can be added to a JCR ruleset in order to define more detailed constraints on JSON messages. Status of This Memo This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet- Drafts is at http://datatracker.ietf.org/drafts/current/. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." This Internet-Draft will expire on September 22, 2016. Copyright Notice Copyright (c) 2016 IETF Trust and the persons identified as the document authors. All rights reserved. This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect Cordell & Newton Expires September 22, 2016 [Page 1] Internet-Draft JCR Co-Constraints March 2016 to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License. 1. Introduction JSON Content Rules [JCR] provides a powerful, intuitive and concise method for defining the structure of JSON [RFC7159] messages. In addition to describing the overall structure of JSON messages, JCR aims to capture the constraints that are imposed on individual items within a message. However, modern JSON usage occasionally requires constraints that can't be expressed by JCR alone. JCR Co-Constraints (JCRCC) defines additional JCR directives and annotations that can be added to a JCR ruleset in order to define more detailed constraints on items within a JSON message, and also supports specifying constraints that depend on the relationship of multiple JSON items. JCRCC constraints represent an additional layer of validation on top of the validation provided by JCR alone. JCRCC constraints may indicate that a JSON instance that was determined to be valid by the rules of a JCR ruleset is in fact invalid. However, if the JCR ruleset indicates that the JSON instance is invalid, JCRCC constraints can not override that and declare the instance to be valid. A JCR processor MAY ignore the JCRCC annotations and directives, perhaps only issuing a warning for encountering an unknown annotation or directive. JCRCC uses the annotations @{id}, @{when} and @{assert} along with the directive #{constraint}. The @{id} annotation is used to identify an item in a JSON message that contributes to the assessment of a JSON instance's validity. The other three each include a 'condition' expression that yields a Boolean true or false result. The validity of the JSON instance is dependent on the results of the various condition expressions. Condition expressions are made up of identifiers, comparators, combiners and functions. Each of these aspects is described in more detail below. 2. Definitions Assessment - The process whereby it is determined whether a JSON instance is valid according to a JCR ruleset (which may or may not include JCR co-constraints). JSON instance - A JSON message that is being validated against a JCR ruleset (which may augmented using JCRCC). Cordell & Newton Expires September 22, 2016 [Page 2] Internet-Draft JCR Co-Constraints March 2016 JSON instance item - An object member or a value in a JSON instance. Often JCRCC annotations will define an identifier that will be associated with a JSON instance item. 3. Annotations and Directives JCRCC uses the annotations @{id}, @{when} and @{assert} plus the directive #{constraint}. 3.1. The @{id} Annotation The @{id} annotation creates an identifier for a rule in a JCR ruleset. A maximum of one @{id} annotation is permitted per rule. It has the form: @{id name} where 'name' corresponds to the 'name' production in the JCR ABNF. The @{id} annotation associates an identifier with the rule on which it is placed. The identifier can then be used in condition expressions to reference the corresponding item in JSON instance items that are mapped to the JCR rule during validation. For example, a JCR rule of: "type" @{id t} : string might associate the identifier 't' with a JSON instance item such as: "type" : "shutdown" 3.2. The @{when} Annotation The @{when} annotation has two similar roles. If a JCR rule indicates that a JSON instance item is optional, then it can be used to describe the conditions when the item is present or absent. Similarly, if a JCR rule indicates that an item has a choice of types, then the @{when} annotation can be used to indicate which of the possible sub-rules is applicable in the current validation instance. Only one @{when} annotation per rule is permitted. The @{when} annotation includes a single 'condition'. In the case of using the @{when} annotation with an optional instance, if the condition yields a 'true' result, then the JSON instance item associated with the JCR rule MUST be present, otherwise it MUST be absent. Cordell & Newton Expires September 22, 2016 [Page 3] Internet-Draft JCR Co-Constraints March 2016 When the @{when} annotation is used to select the applicable member/ type rule within a group or type choice, the condition of each @{when} annotation is evaluated in turn (from left to right as shown in the JCR rule) and the member/type rule that corresponds to the first @{when} condition that yields a 'true' result is selected. If none of the @{when} annotations on a group or type choice yields true, this indicates an invalid instance. When a member/type rule within a group or type choice that has @{when} annotations on other members/types, but does not itself have an @{when} annotation, this indicates the default case. In essence, if a rule contains @{when} annotations, then an absent @{when} annotation on a member/type rule is equivalent to @{when true}. As an example, a @{when} annotation on an optional item may look as follows: ? @{when $t == "shutdown"} "uptime" : integer This indicates that the "uptime" member should be present if the JSON instance item associated with a JCR rule with an @{id t} annotation has the value "shutdown". A @{when} annotation on a group may look as follows: details ( @{when $t == "boot"} boot-details | @{when $t == "shutdown"} shutdown-details | default-details ) This indicates that the JCR rule named 'boot-details' is applicable when the JSON instance item associated with an @{id t} annotation has the value "boot", the rule 'shutdown-details' is applicable when the value of the $t item is "shutdown", otherwise the rule 'default- details' is applicable. (The rules identified by 'boot-details', 'shutdown-details' and 'default-details' might be groups that act as mixins for the rule in which the 'details' rule is used.) The @{when} annotation can reference identifiers in siblings, ancestors, and descendants. To avoid circular or ambiguous dependencies, the identifiers in descendants must not be part of arrays or descendants of itself or descendants of siblings that have @{when} annotations. The latter restriction avoids needing to know whether a secondary @{when} annotation yields 'true' in order to determine if the @{when} annotation being assessed yields 'true'. When seeking identifiers, siblings are inspected first, followed by the nearest ancestor, followed by the nearest descendent. If it is desired to look for an identifier that is a descendent without first looking for an identifier that is an ancestor, then the Cordell & Newton Expires September 22, 2016 [Page 4] Internet-Draft JCR Co-Constraints March 2016 'descendent()' method can be called on the name of the identifier. For example: ? @{when descendent($s) == "on"} "watts" : integer 3.3. The @{assert} Annotation The @{assert} annotation is used to specify additional constraints on an item that can't be expressed using JCR alone. The @{assert} annotation contains a single condition that must yield 'true' for the JSON instance item to be considered valid. A maximum of one @{assert} annotations is permitted per rule. @{assert} annotations are evaluated after all sibling @{when} annotations have been evaluated, and constraints specified by the underlying JCR rule have been assessed. An item may have both a @{when} annotation and a @{assert} annotation. If the condition in the @{when} annotation yields 'false', then the item it corresponds to should be absent in the JSON instance, so the @{assert} condition is not evaluated. If the JSON instance item is not valid according the underlying JCR rule, then validation fails at that point and the @{assert} annotation is not assessed. When seeking identifiers referenced in an @{assert} annotation, siblings are inspected first, followed by the nearest ancestor, followed by the nearest descendent. If it is desired to look for an identifier that is a descendent without first looking for an identifier that is an ancestor, then the 'descendent()' method can be called on the name of the identifier. An example @{assert} annotation might be: "index" @{assert $ % 2 == 0} : integer ; Must be even 3.4. The #{constraint} Directive The #{constraint} directive offers a way to express conditions external to @{when} and @{assert} annotations. #{constraint} directives can be viewed as a macro substitution mechanism. @{when} and @{assert} annotations, and other #{constraint} directives can reference conditions defined by a #{constraint}. The format of a #{constraint} directive is as follows: #{constraint name condition} where 'name' corresponds to the 'name' production in the JCR ABNF, and the 'condition' is the same as used in @{when} and @{assert} annotations and is as described below. Cordell & Newton Expires September 22, 2016 [Page 5] Internet-Draft JCR Co-Constraints March 2016 Conceptually at least, the 'condition' in a #{constraint} directive is substituted into @{when}, @{assert} annotations and other #{constraint} directives wherever the constraint's 'name' is referenced. (In practice, for the purposes of efficiency, the result of a #{constraint} directive may be cached or memoized, to avoid repeated computation of the sub-condition. However, such optimizations are beyond the scope of this document.) An example usage, equating to the earlier example, might be: #{constraint is_even $ % 2 == 0} "index" @{assert @is_even} : integer ; Must be even 4. Conditions The @{when} annotation, @{assert} annotation and #{constraint} directive contain 'conditions'. These are made up of 'identifiers', 'comparators', 'combiners' and 'functions' as described below. 4.1. Identifiers Identifiers are used to refer to items in the JCR, and #{constraint} directives. They have a few different forms. '$' on its own refers to the member / type expressed by the current JCR rule. For example: int-pairs @{assert count( $ ) % 2 == 0} [ : integer ] The form '$name' and '$alias.name' form is an item reference and refers to members and types identified by JCR rules by @{id} annotations. An 'alias' is set up using the normal JCR #import directive and allows members / types outside the current ruleset to be identified. For example: @{id type} : string, { ? "uptime" @{when $type == "shutdown"} : integer } An item reference that is not part of an 'operator' or a 'comparator' sub-expression yields 'true' if the referenced item is present in the JSON instance being validated, and 'false' if not. For example, the following says that the 'dob' member must be present if the 'name' member is present: ? "name" @{id n} : string, ? "dob" @{when $n} : full-date Cordell & Newton Expires September 22, 2016 [Page 6] Internet-Draft JCR Co-Constraints March 2016 An item reference that is part of an 'operator' or a 'comparator' sub-expression yields the value of the corresponding JSON instance item. Item references may also be used as arguments to functions. The '@name' and '@alias.name' form is a constraint reference and refers to a condition expressed in a #{constraint} directive. An 'alias' is set up using the normal JCR #import directive and allows constraints outside the current ruleset to be identified. For example: #{constraint is_even $ % 2 == 0} "index" : @{assert @is_even} integer ; Must be even 4.2. Operators The values of JSON instance items identified by identifiers, values yielded by other 'operators' and values returned by 'functions' can be subject to computations using 'operators'. The supported operators are '+', '-', '*', '/' and '%'. They have their usual C-family programming language meaning. The precedence of the operators is as-per normal mathematics rules. Operators have higher precedence than comparators. 4.3. Comparators The values of JSON instance items identified by identifiers, values yielded by 'operators' and values returned by 'functions' can be compared using 'comparators'. The comparators are the usual '<', '<=', '==', '!=', '>=' and '>', and have their usual C-family language meaning. Comparators yield a 'true' or 'false' result. When an identifier referenced by a comparator is absent, then the comparison returns 'false'. For example: $t == "boot" is equivalent to: ( $t && $t == "boot" ) Similarly: $t == "boot" || $other == "close" is equivalent to: Cordell & Newton Expires September 22, 2016 [Page 7] Internet-Draft JCR Co-Constraints March 2016 ( $t && $t == "boot" ) || ( $other && $other == "close" ) And: length( $first ) > length( $second ) is equivalent to: ( $first && $second && length( $first ) > length( $second ) ) Comparators have higher precedence than combiners. 4.4. Combiners Multiple results of 'comparators' or standalone identifiers can be combined using 'combiners'. The supported combiners are '&&' and '||'. They have their usual C-family programming language meaning. 4.5. Functions JCRCC supports a number of functions that can be used to yield specific information about a JSON instance item referenced by an identifier. Some functions can operate on multiple types of argument, such as identifiers or strings. In the function descriptions below, arguments that can take multiple different types have each type listed, separated by the pipe symbol (|). For example, an argument description of "identifier | string" indicates that the function can take an identifier or a string as an argument. The functions are as follows: name( identifier ) - Returns the member name of the JSON instance item associated with the identifier as a string. length( identifier | string ) - If the argument is an identifier, the value of the JSON instance item associated with it MUST be represented using a JSON string (i.e. it could be defined as a JCR ip4 type that is represented in JSON using the string format). The function returns the length of the string in Unicode code points. To return the length of a JSON instance item's member name, do "length( name( $t ) )". count( identifier ) - The JSON instance item associated with the identifier MUST be an array. The function returns the number of items in the array. Cordell & Newton Expires September 22, 2016 [Page 8] Internet-Draft JCR Co-Constraints March 2016 capture( identifier | string, regex ) - The regex in the capture function MUST include a capture expression (i.e. a suitable term in brackets). The regex is applied to the input string, or the string value of the JSON instance item associated with the identifier, and the sub- string captured by the regex capture expression is returned. descendent( identifier ) - The normal order of identifier look up is, siblings, followed by ancestors, followed by descendents. This function will cause the lookup to be in the order siblings followed by descendents. It returns a reference to a JSON instance item that can be used in place of an identifier. For example, "length( name( descendent( $t ) ) )". error( q_string ) - This function can be used for reporting error messages. The text in the q_string may be subject to value interpolation and internationalization by an implementation, but this is not required. It always returns false. For example: @{assert $%2==0 || error("value must be even")} :integer is_null( identifier ), is_boolean( identifier ), is_string( identifier ), is_float( identifier ), is_integer( identifier ), is_ip4( identifier ), is_ip6( identifier ), is_fqdn( identifier ), is_idn( identifier ), is_uri( identifier ), is_phone( identifier ), is_email( identifier ), is_full-date( identifier ), is_full-time( identifier ), is_date-time( identifier ), is_base64( identifier ) - This set of functions return true if the JSON instance item associated with the identifier has the corresponding type, and false otherwise. 4.6. If-Then-Else 5. ABNF The ABNF is 'work in progress'. It currently looks as below. This does not capture where spaces are permitted. Cordell & Newton Expires September 22, 2016 [Page 9] Internet-Draft JCR Co-Constraints March 2016 condition = relational ( * ( "&&" relational ) / * ( "||" relational ) ) relational = ["!"] value / value comparator value / ["!"] condition-group / ternary value = identifier / constant / function / "@" [ alias "." ] name identifier = "$" / "$" [ alias "." ] name constant = "null" / "true" / "false" / integer / float / q_string / regex comparator = "==" / "!=" / "<" / "<=" / ">=" / ">" condition-group = "(" condition ")" ternary = "if" "(" condition ")" "then" "(" condition ")" "else" "(" condition ")" function = "name" "(" identifier ")" / "length" "(" identifier ")" / "count" "(" identifier ")" / "capture" "(" regex "," identifier ")" / "descendent" "(" identifier ")" / "error" "(" q_string ")" / "is_integer" "(" identifier ")" / "is_float" "(" identifier ")" / etc... 6. References 6.1. Normative References [JCR] Newton, A. and P. Cordell, "A Language for Rules Describing JSON Content", October 2015, . [RFC7159] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", RFC 7159, DOI 10.17487/RFC7159, March 2014, . 6.2. Infomative References Cordell & Newton Expires September 22, 2016 [Page 10] Internet-Draft JCR Co-Constraints March 2016 [ARIN_JCR_VALIDATOR] American Registry for Internet Numbers, "JSON Content Rules Validator (Work In Progress)", . [CODALOGIC_JCR_VALIDATOR] Codalogic, "cl-jcr-parser (Work In Progress)", . Appendix A. JCR Implementations The following implementations, [ARIN_JCR_VALIDATOR] and [CODALOGIC_JCR_VALIDATOR] have influenced the development of this document. Authors' Addresses Pete Cordell Codalogic PO Box 30 Ipswitch IP5 2WY UK Email: pete.cordell@codalogic.com URI: http://www.codalogic.com Andrew Lee Newton American Registry for Internet Numbers 3635 Concorde Parkway Chantilly, VA 20151 US Email: andy@arin.net URI: http://www.arin.net Cordell & Newton Expires September 22, 2016 [Page 11]