Commit a2a6ea17 authored by Mark Canterbury's avatar Mark Canterbury
Browse files

TS 103 280 CR023 - JSON

parent 3706f457
Loading
Loading
Loading
Loading
+389 −0
Original line number Original line Diff line number Diff line
{
  "$id": "ts_103280_2017_07",
  "$defs": {
    "ShortString": {
      "type": "string",
      "maxLength": 255
    },
    "LongString": {
      "type": "string",
      "maxLength": 65535
    },
    "LIID": {
      "type": "string",
      "pattern": "^([!-~]{1,25})|([0-9a-f]{26,50})$"
    },
    "UTCDateTime": {
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$"
    },
    "UTCMicrosecondDateTime": {
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{6}Z$"
    },
    "QualifiedDateTime": {
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:[0-9]{2})$"
    },
    "QualifiedMicrosecondDateTime": {
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{6}(Z|[+-][0-9]{2}:[0-9]{2})$"
    },
    "InternationalE164": {
      "type": "string",
      "pattern": "^[0-9]{1,15}$"
    },
    "IMSI": {
      "type": "string",
      "pattern": "^[0-9]{6,15}$"
    },
    "IMEI": {
      "type": "string",
      "pattern": "^[0-9]{14}$"
    },
    "IMEICheckDigit": {
      "type": "string",
      "pattern": "^[0-9]{15}$"
    },
    "IMEISV": {
      "type": "string",
      "pattern": "^[0-9]{16}$"
    },
    "IPv4Address": {
      "type": "string",
      "pattern": "^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$"
    },
    "IPv4CIDR": {
      "type": "string",
      "pattern": "^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])/([1-2]?[0-9]|3[0-2])$"
    },
    "IPv6Address": {
      "type": "string",
      "pattern": "^([0-9a-f]{4}:){7}([0-9a-f]{4})$"
    },
    "IPv6CIDR": {
      "type": "string",
      "pattern": "^([0-9a-f]{4}:){7}([0-9a-f]{4})/(([1-9][0-9]?)|(1[0-1][0-9])|(12[0-8]))$"
    },
    "TCPPort": {
      "type": "integer",
      "exclusiveMinimum": 1,
      "maximum": 65535
    },
    "UDPPort": {
      "type": "integer",
      "minimum": 0,
      "maximum": 65535
    },
    "MACAddress": {
      "type": "string",
      "pattern": "^([a-f0-9]{2}:){5}[a-f0-9]{2}$"
    },
    "EmailAddress": {
      "allOf": [
        {
          "$ref": "#/$defs/ShortString"
        },
        {
          "type": "string",
          "pattern": "^[a-zA-Z0-9\\.!#$%&'\\*\\+\\\\/=\\?\\^_`\\{\\|\\}~\\-]+@[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
        }
      ]
    },
    "UUID": {
      "type": "string",
      "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$"
    },
    "ISOCountryCode": {
      "type": "string",
      "pattern": "^[A-Z]{2}$"
    },
    "SIPURI": {
      "type": "string",
      "pattern": "^sips?:[a-zA-Z0-9!#$&-;=?-\\[\\]_~%]+$"
    },
    "TELURI": {
      "type": "string",
      "pattern": "^tel:[a-zA-Z0-9!#$&-;=?-\\[\\]_~%]+$"
    },
    "WGS84LatitudeDecimal": {
      "type": "string",
      "pattern": "^[NS][0-9]{2}\\.[0-9]{6}$"
    },
    "WGS84LongitudeDecimal": {
      "type": "string",
      "pattern": "^[EW][0-9]{3}\\.[0-9]{6}$"
    },
    "WGS84LatitudeAngular": {
      "type": "string",
      "pattern": "^[NS][0-9]{6}\\.[0-9]{2}$"
    },
    "WGS84LongitudeAngular": {
      "type": "string",
      "pattern": "^[EW][0-9]{7}\\.[0-9]{2}$"
    },
    "SUPIIMSI": {
      "$ref": "#/$defs/IMSI"
    },
    "SUPINAI": {
      "$ref": "#/$defs/NAI"
    },
    "SUCI": {
      "type": "string",
      "pattern": "^([a-fA-F0-9]{2})*$"
    },
    "PEIIMEI": {
      "$ref": "#/$defs/IMEI"
    },
    "PEIIMEICheckDigit": {
      "$ref": "#/$defs/IMEICheckDigit"
    },
    "PEIIMEISV": {
      "$ref": "#/$defs/IMEISV"
    },
    "GPSIMSISDN": {
      "type": "string",
      "pattern": "^[0-9]{1,15}$"
    },
    "GPSINAI": {
      "$ref": "#/$defs/NAI"
    },
    "NAI": {
      "type": "string"
    },
    "LDID": {
      "type": "string",
      "pattern": "^([A-Z]{2}-.+-.+)$"
    },
    "InternationalizedEmailAddress": {
      "allOf": [
        {
          "$ref": "#/$defs/ShortString"
        },
        {
          "type": "string",
          "pattern": "^.+@.+$"
        }
      ]
    },
    "EUI64": {
      "type": "string",
      "pattern": "^([a-f0-9]{2}:){7}[a-f0-9]{2}$"
    },
    "CGI": {
      "type": "string",
      "pattern": "^[0-9]{3}-[0-9]{2,3}-[a-f0-9]{4}-[a-f0-9]{4}$"
    },
    "ECGI": {
      "type": "string",
      "pattern": "^[0-9]{3}-[0-9]{2,3}-[a-f0-9]{7}$"
    },
    "NCGI": {
      "type": "string",
      "pattern": "^[0-9]{3}-[0-9]{2,3}-[a-f0-9]{9}$"
    },
    "ICCID": {
      "type": "string",
      "pattern": "^[0-9]{19,20}$"
    },
    "IPProtocol": {
      "type": "integer",
      "minimum": 0,
      "maximum": 255
    },
    "IPAddress": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "etsi280:IPv4Address": {
              "$ref": "#/$defs/IPv4Address"
            }
          },
          "required": [
            "etsi280:IPv4Address"
          ]
        },
        {
          "type": "object",
          "properties": {
            "etsi280:IPv6Address": {
              "$ref": "#/$defs/IPv6Address"
            }
          },
          "required": [
            "etsi280:IPv6Address"
          ]
        }
      ]
    },
    "IPCIDR": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "etsi280:IPv4CIDR": {
              "$ref": "#/$defs/IPv4CIDR"
            }
          },
          "required": [
            "etsi280:IPv4CIDR"
          ]
        },
        {
          "type": "object",
          "properties": {
            "etsi280:IPv6CIDR": {
              "$ref": "#/$defs/IPv6CIDR"
            }
          },
          "required": [
            "etsi280:IPv6CIDR"
          ]
        }
      ]
    },
    "TCPPortRange": {
      "type": "object",
      "properties": {
        "etsi280:start": {
          "$ref": "#/$defs/TCPPort"
        },
        "etsi280:end": {
          "$ref": "#/$defs/TCPPort"
        }
      },
      "required": [
        "etsi280:start",
        "etsi280:end"
      ]
    },
    "UDPPortRange": {
      "type": "object",
      "properties": {
        "etsi280:start": {
          "$ref": "#/$defs/UDPPort"
        },
        "etsi280:end": {
          "$ref": "#/$defs/UDPPort"
        }
      },
      "required": [
        "etsi280:start",
        "etsi280:end"
      ]
    },
    "Port": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "etsi280:TCPPort": {
              "$ref": "#/$defs/TCPPort"
            }
          },
          "required": [
            "etsi280:TCPPort"
          ]
        },
        {
          "type": "object",
          "properties": {
            "etsi280:UDPPort": {
              "$ref": "#/$defs/UDPPort"
            }
          },
          "required": [
            "etsi280:UDPPort"
          ]
        }
      ]
    },
    "PortRange": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "etsi280:TCPPortRange": {
              "$ref": "#/$defs/TCPPortRange"
            }
          },
          "required": [
            "etsi280:TCPPortRange"
          ]
        },
        {
          "type": "object",
          "properties": {
            "etsi280:UDPPortRange": {
              "$ref": "#/$defs/UDPPortRange"
            }
          },
          "required": [
            "etsi280:UDPPortRange"
          ]
        }
      ]
    },
    "IPAddressPort": {
      "type": "object",
      "properties": {
        "etsi280:address": {
          "$ref": "#/$defs/IPAddress"
        },
        "etsi280:port": {
          "$ref": "#/$defs/Port"
        }
      },
      "required": [
        "etsi280:address",
        "etsi280:port"
      ]
    },
    "IPAddressPortRange": {
      "type": "object",
      "properties": {
        "etsi280:address": {
          "$ref": "#/$defs/IPAddress"
        },
        "etsi280:portRange": {
          "$ref": "#/$defs/PortRange"
        }
      },
      "required": [
        "etsi280:address",
        "etsi280:portRange"
      ]
    },
    "WGS84CoordinateDecimal": {
      "type": "object",
      "properties": {
        "etsi280:latitude": {
          "$ref": "#/$defs/WGS84LatitudeDecimal"
        },
        "etsi280:longitude": {
          "$ref": "#/$defs/WGS84LongitudeDecimal"
        }
      },
      "required": [
        "etsi280:latitude",
        "etsi280:longitude"
      ]
    },
    "WGS84CoordinateAngular": {
      "type": "object",
      "properties": {
        "etsi280:latitude": {
          "$ref": "#/$defs/WGS84LatitudeAngular"
        },
        "etsi280:longitude": {
          "$ref": "#/$defs/WGS84LongitudeAngular"
        }
      },
      "required": [
        "etsi280:latitude",
        "etsi280:longitude"
      ]
    }
  }
}
 No newline at end of file

utils/json_to_xml.py

0 → 100644
+35 −0
Original line number Original line Diff line number Diff line
import sys
import logging
from pprint import pprint
import json
from pathlib import Path
import fileinput

import xmltodict
import argparse


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', '--verbose', action='count', help='Verbose logging (can be specified multiple times)')
    parser.add_argument('-i', '--input', type=argparse.FileType('r'), default=sys.stdin, help="Path to input file (if absent, stdin is used)")
    args = parser.parse_args()

    match args.verbose:
        case v if v and v >= 2:
            logging.basicConfig(level=logging.DEBUG)
        case 1:
            logging.basicConfig(level=logging.INFO)
        case _:
            logging.basicConfig(level=logging.WARNING)

    logging.debug(f"Arguments: {args}")

    s = args.input.read()
    args.input.close()

    logging.debug(s)
    j = json.loads(s)

    xml = xmltodict.unparse({'HI1Message' : j}, )
    print(xml)
 No newline at end of file
+47 −0
Original line number Original line Diff line number Diff line
import logging

from xmlschema.validators.simple_types import *
from xmlschema.validators.complex_types import *
from xmlschema.validators.groups import *
from xmlschema.validators.facets import *

from .TypeMapping import TypeMapping
from .ComplexTypeMapping import ComplexTypeMapping

log = logging.getLogger()

class ChoiceMapping(ComplexTypeMapping):
    @classmethod
    def process_choice(cls, choice: XsdGroup, current_ns : str, ns_to_id_map):
        if choice.model != 'choice':
            raise Exception(f"Wrong group type: {c.model}")
        oneOf = []
        for c in choice.iter_model():
            if not (type(c) is XsdElement):
                raise Exception (f"Non-element {c} encountered in choice {choice}")
            element_name = c.local_name
            if c.target_namespace in ns_to_id_map:
                ns = ns_to_id_map[c.target_namespace]
                if 'prefix' in ns:
                    element_name = ns['prefix'] + ":" + element_name            
            t = TypeMapping.get_type_from_elem(c, current_ns)
            oneOf.append({
                "type" : "object",
                "properties" : {
                    element_name : t
                },
                "required" : [element_name]
            })
        return oneOf   

    def map(self, xst : BaseXsdType):
        log.debug(f"Attempting mapping of {xst} to choice")
        j = super().map(xst)
        if j is None:
            log.debug("Not a complex type, giving up")
            return None
        content = xst.content
        if (content.model != 'choice'):
            log.debug("Not a choice, giving up")
            return None
        return { 'oneOf' : ChoiceMapping.process_choice(content, xst.namespaces[''], self.ns_to_id_map)}
+11 −0
Original line number Original line Diff line number Diff line
from xmlschema.validators.complex_types import *

from .TypeMapping import TypeMapping

class ComplexTypeMapping(TypeMapping):
    def map(self, xst: BaseXsdType):
        if not (type(xst) is XsdComplexType):
            return None
        return {
            "type" : "object"
        }
+85 −0
Original line number Original line Diff line number Diff line
import logging

from xmlschema.validators.simple_types import *
from xmlschema.validators.complex_types import *
from xmlschema.validators.groups import *
from xmlschema.validators.facets import *

from .TypeMapping import TypeMapping
from .ChoiceMapping import ChoiceMapping
from .ComplexTypeMapping import ComplexTypeMapping

log = logging.getLogger()


class SequenceMapping(ComplexTypeMapping):
    def map(self, xst: BaseXsdType):
        log.debug(f"Attempting mapping of {xst} to sequence")
        j = super().map(xst)
        if j is None:
            log.debug("Not a complex type, giving up")
            return None
        content = xst.content
        if (content.model != 'sequence'):
            log.debug("Not a sequence, giving up")
            return None
        mapped_type = {
            'type' : 'object',
            'properties' : {},
            'required' : []
        }

        # Not going to try and do all of this automatically for now
        # Only make insert the xsiType parameter
        if (xst.base_type):
            # mapped_type['__DESCENDENT_OF__'] = TypeMapping.get_ref_for(xst.base_type, xst.namespaces[''])
            mapped_type['properties']['@xsi:type'] = {
                "type" : "string",
                "enum" : xst.name
            }
            mapped_type['required'].append('@xsi:type')
        # if xst.abstract:
        #     mapped_type['__ABSTRACT__'] = True
        #     pass

        inner_choice = None
        for c in list(content.iter_model()):
            log.debug(f"Processing model item {c}")
            if type(c) is XsdElement:
                element_name = c.local_name
                if c.target_namespace in self.ns_to_id_map:
                    ns = self.ns_to_id_map[c.target_namespace]
                    if 'prefix' in ns:
                        element_name = ns['prefix'] + ":" + element_name
                if c.effective_max_occurs != 1:
                    mapped_type['properties'][element_name] = {
                        "type" : "array",
                        "items" : TypeMapping.get_type_from_elem(c, xst.namespaces[''])
                    }
                    if c.effective_max_occurs:
                         mapped_type['properties'][element_name]['maxItems'] = c.effective_max_occurs
                    if c.effective_min_occurs > 0:
                         mapped_type['properties'][element_name]['minItems'] = c.effective_min_occurs
                else:
                    mapped_type['properties'][element_name] = TypeMapping.get_type_from_elem(c, xst.namespaces[''])
                    if c.effective_min_occurs == 1:
                        mapped_type['required'].append(element_name)
            elif type(c) is XsdGroup:
                if inner_choice:
                    raise Exception (f"Second group '{element_name}' encountered in {xst}")
                if c.model != "choice":
                    raise Exception (f"Don't know what to do with inner group {c} in {xst} - not a choice")
                inner_choice = ChoiceMapping.process_choice(c, xst.namespaces[''], self.ns_to_id_map)
            elif type(c) is XsdAnyElement:
                mapped_type = {}
            else:
                raise Exception(f"Unknown element type {c}")
        if (inner_choice):
            return { 
                'allOf' : [
                    mapped_type,
                    {'oneOf' : inner_choice}
                ]
            }
        else:
            return mapped_type
Loading