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

Merge branch 'cr/103280/023' into 'meeting/LI63'

TS 103 280 CR023 - JSON

See merge request !84
parents 3706f457 a2a6ea17
Loading
Loading
Loading
Loading
Loading
+389 −0
Original line number 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 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 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 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 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