NTV.json_ntv.ntv

@author: Philippe@loco-labs.io

The ntv module is part of the NTV.json_ntv package (specification document).

It contains the classes NtvSingle, NtvList, Ntv(abstract) for NTV entities.

For more information, see the user guide or the github repository.

   1# -*- coding: utf-8 -*-
   2"""
   3@author: Philippe@loco-labs.io
   4
   5The `ntv` module is part of the `NTV.json_ntv` package ([specification document](
   6https://loco-philippe.github.io/ES/JSON%20semantic%20format%20(JSON-NTV).htm)).
   7
   8It contains the classes `NtvSingle`, `NtvList`, `Ntv`(abstract) for NTV entities.
   9
  10For more information, see the
  11[user guide](https://loco-philippe.github.io/NTV/documentation/user_guide.html)
  12or the [github repository](https://github.com/loco-philippe/NTV).
  13
  14"""
  15import copy
  16from abc import ABC, abstractmethod
  17from numbers import Number
  18import json
  19
  20from json_ntv.namespace import Datatype, Namespace, str_type, relative_type, agreg_type
  21from json_ntv.ntv_util import NtvError, NtvJsonEncoder, NtvConnector, NtvTree, NtvUtil
  22from json_ntv.ntv_patch import NtvPointer
  23
  24NAME = 'N'
  25TYPE = 'T'
  26VALUE = 'V'
  27ENTITY = 'E'
  28NTVSINGLE = 'S'
  29NTVLIST = 'L'
  30
  31
  32class Ntv(ABC, NtvUtil):
  33    ''' The Ntv class is an abstract class used by `NtvSingle`and `NtvList` classes.
  34
  35    *Attributes :*
  36    - **ntv_name** :  String - name of the NTV entity
  37    - **ntv_type**:   Datatype or Namespace - type of the entity
  38    - **ntv_value**:  value of the entity
  39
  40    *Internal attributes :*
  41    - **parent**:     parent NtvList entity
  42    - **is_json**:    True if ntv_value is a json_value
  43
  44    *dynamic values (@property)*
  45    - `code_ntv`
  46    - `json_name_str`
  47    - `json_array` (abstract method)
  48    - `max_len`
  49    - `name`
  50    - `tree`
  51    - `type_str`
  52    - `val`
  53
  54    The methods defined in this class are :
  55
  56    *Ntv constructor (staticmethod)*
  57    - `fast`
  58    - `obj`
  59    - `from_obj`
  60    - `from_att`
  61
  62    *NTV conversion (instance methods)*
  63    - `alike`
  64    - `to_json_ntv`
  65    - `to_obj_ntv`
  66    - `validate` (validation)
  67
  68    *export - conversion (instance methods)*
  69    - `expand`
  70    - `no_type`
  71    - `no_name`
  72    - `reduce`
  73    - `to_fast`
  74    - `to_name`
  75    - `to_obj`
  76    - `to_repr`
  77    - `to_mermaid`
  78    - `to_tuple`
  79    - `to_ntvsingle`
  80    - `to_ntvlist`
  81
  82    *tree methods (instance methods)*
  83    - `childs`
  84    - `pointer`
  85    - `replace`
  86    - `remove`
  87    - `append` (NtvList only)
  88    - `insert` (NtvList only)
  89
  90    *other instance methods*
  91    - `from_value`
  92    - `json_name`
  93    - `set_name`
  94    - `set_type`
  95    - `set_value`
  96    - `obj_value` (abstract method)
  97
  98    *utility methods*
  99    - `decode_json` *(staticmethod)*
 100    - `obj_ntv` *(staticmethod)*
 101    '''
 102
 103    def __init__(self, ntv_value, ntv_name, ntv_type):
 104        '''Ntv constructor.
 105
 106        *Parameters*
 107
 108        - **ntv_value**: Json entity - value of the entity
 109        - **ntv_name** : String (default None) - name of the NTV entity
 110        - **ntv_type**: String or Datatype or Namespace (default None) - type of the entity
 111        '''
 112        if ntv_type.__class__.__name__ in ['Datatype', 'Namespace']:
 113            self.ntv_type = ntv_type
 114        elif ntv_type and ntv_type[-1] != '.':
 115            self.ntv_type = Datatype(ntv_type)
 116        elif ntv_type and ntv_type[-1] == '.':
 117            self.ntv_type = Namespace.add(ntv_type)
 118        else:
 119            self.ntv_type = None
 120        if not isinstance(ntv_name, str):
 121            ntv_name = ''
 122        self.ntv_name = ntv_name
 123        self.ntv_value = ntv_value
 124        self.is_json = NtvConnector.is_json(ntv_value)
 125        self.parent = None
 126
 127    @staticmethod
 128    def fast(data, no_typ=False, typ_auto=False):
 129        ''' return an Ntv entity from data without conversion.
 130
 131        *Parameters* : see `obj` method'''
 132        return Ntv.obj(data, no_typ=no_typ, typ_auto=typ_auto, fast=True)
 133
 134    @staticmethod
 135    def obj(data, no_typ=False, decode_str=False, typ_auto=False, fast=False):
 136        ''' return an Ntv entity from data.
 137
 138        *Parameters*
 139
 140        - **Data** can be :
 141            - a tuple with value, name, typ and cat (see `from_att` method)
 142            - a value to decode (see `from_obj`method)
 143        - **no_typ** : boolean (default False) - if True, NtvList is with None type
 144        - **type_auto**: boolean (default False) - if True, default type for NtvList
 145        is the ntv_type of the first Ntv in the ntv_value
 146        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
 147        - **decode_str**: boolean (default False) - if True, string are loaded in json data'''
 148        if isinstance(data, tuple):
 149            return Ntv.from_att(*data, decode_str=decode_str, fast=fast)
 150        if isinstance(data, str):
 151            try:
 152                data = json.loads(data)
 153            except json.JSONDecodeError:
 154                pass
 155        return Ntv.from_obj(data, no_typ=no_typ, decode_str=decode_str,
 156                            typ_auto=typ_auto, fast=fast)
 157
 158    @staticmethod
 159    def from_att(value, name, typ, cat, decode_str=False, fast=False):
 160        ''' return an Ntv entity.
 161
 162        *Parameters*
 163
 164        - **value**: Ntv entity or value to convert in an Ntv entity
 165        - **name** : string - name of the Ntv entity
 166        - **typ** : string or NtvType - type of the NTV entity
 167        - **cat**: string - NTV category ('single', 'list')
 168        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
 169        - **decode_str**: boolean (default False) - if True, string are loaded as json data'''
 170
 171        value = Ntv._from_value(value, decode_str)
 172        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
 173            return value
 174        if isinstance(value, list) and cat == 'list':
 175            return NtvList(value, name, typ, fast=fast)
 176        if cat == 'single':
 177            return NtvSingle(value, name, typ, fast=fast)
 178        return Ntv.from_obj(value, def_type=typ, fast=fast)
 179
 180    @staticmethod
 181    def from_obj(value, def_type=None, def_sep=None, no_typ=False, decode_str=False,
 182                 typ_auto=False, fast=False):
 183        ''' return an Ntv entity from an object value.
 184
 185        *Parameters*
 186
 187        - **value**: Ntv value to convert in an Ntv entity
 188        - **no_typ** : boolean (default None) - if True, NtvList is with None type
 189        - **def_type** : Datatype or Namespace (default None) - default type of the value
 190        - **def_sep**: ':', '::' or None (default None) - default separator of the value
 191        - **decode_str**: boolean (default False) - if True, string are loaded as json data
 192        - **type_auto**: boolean (default False) - if True, default type for NtvList
 193        is the ntv_type of the first Ntv in the ntv_value
 194        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion'''
 195        value = Ntv._from_value(value, decode_str)
 196        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
 197            return value
 198        ntv_value, ntv_name, str_typ, sep, is_json = Ntv.decode_json(value)
 199        sep = def_sep if not sep else sep
 200        sep = None if str_typ and str_typ[-1] == '.' and sep == ':' else sep
 201        if isinstance(ntv_value, (list, dict)) and sep in (None, '::'):
 202            return Ntv._create_ntvlist(str_typ, def_type, sep, ntv_value,
 203                                       typ_auto, no_typ, ntv_name, fast)
 204        if sep == ':' or (sep is None and isinstance(ntv_value, dict)):
 205            ntv_type = agreg_type(str_typ, def_type, False)
 206            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
 207        if sep is None and not isinstance(ntv_value, dict):
 208            is_single_json = isinstance(ntv_value, (int, str, float, bool))
 209            ntv_type = agreg_type(str_typ, def_type, is_single_json)
 210            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
 211        raise NtvError('separator ":" is not compatible with value')
 212
 213    def __len__(self):
 214        ''' len of ntv_value'''
 215        if isinstance(self.ntv_value, list):
 216            return len(self.ntv_value)
 217        return 1
 218
 219    def __str__(self):
 220        '''return string format'''
 221        return self.to_obj(encoded=True)
 222
 223    def __repr__(self):
 224        '''return classname and code'''
 225        return self.reduce(obj=False, level=3, maxi=6).to_obj(encoded=True)
 226
 227    def __contains__(self, item):
 228        ''' item of Ntv entities'''
 229        if isinstance(self.val, list):
 230            return item in self.ntv_value
 231        return item == self.ntv_value
 232
 233    def __iter__(self):
 234        ''' iterator for Ntv entities'''
 235        if isinstance(self, NtvSingle):
 236            return iter([self])
 237        return iter(self.val)
 238
 239    def __getitem__(self, selec):
 240        ''' return ntv_value item with selec:
 241            - String beginning with "#" : json-pointer,
 242            - string : name of the ntv,
 243            - list : recursive selector
 244            - tuple : list of name or index '''
 245        if selec is None or selec == '':
 246            return self
 247        if isinstance(selec, NtvPointer) or isinstance(selec, str) and selec[0] == '#':
 248            selec = self._pointer_to_list(selec)
 249        if selec in ([], ()):
 250            return self
 251        if isinstance(selec, (list, tuple)) and len(selec) == 1:
 252            selec = selec[0]
 253        if selec in (0, self.ntv_name) and isinstance(self, NtvSingle):
 254            return self.ntv_value
 255        if isinstance(self, NtvSingle):
 256            raise NtvError('item not present')
 257        if isinstance(selec, tuple):
 258            return [self[i] for i in selec]
 259        if isinstance(selec, str):
 260            return self.ntv_value[self._string_to_ind(selec)]
 261        if isinstance(selec, list):
 262            return self[selec[0]][selec[1:]]
 263        return self.ntv_value[selec]
 264
 265    def _pointer_to_list(self, pointer):
 266        '''return a list of child pointers from a string or a NtvPointer'''
 267        if isinstance(pointer, str):
 268            selec = list(NtvPointer(pointer[1:]))
 269        elif isinstance(pointer, NtvPointer):
 270            selec = list(pointer)
 271        else:
 272            raise NtvError('pointer is not a valid pointer')
 273        if not (selec[0] in [self.json_name(string=True), self.ntv_name]
 274                or (isinstance(selec[0], int) and selec[0] == 0)):
 275            raise NtvError(
 276                str(selec[0]) + 'is not the root json_name : ' + self.ntv_name)
 277        return selec[1:]
 278
 279    def _string_to_ind(self, json_name):
 280        '''return the index of a name or a json_name'''
 281        if NtvUtil.from_obj_name(json_name)[1]:
 282            return [ntv.json_name(def_type=self.type_str, string=True)
 283                    for ntv in self.ntv_value].index(json_name)
 284        return [ntv.ntv_name for ntv in self.ntv_value].index(json_name)
 285
 286    def __lt__(self, other):
 287        ''' return a comparison between two ntv_value'''
 288        # order: number > string > None
 289        res = Ntv.lower(self, other)
 290        if res is None:
 291            res = None if len(self) == len(other) else len(self) < len(other)
 292        res = self.to_obj(encoded=True) < other.to_obj(
 293            encoded=True) if res is None else res
 294        return res
 295
 296    @property
 297    def code_ntv(self):
 298        '''return a string with the NTV code composed with 1 to 3 letters:
 299        - 'l' (NtvList), 's' (NtvSingle / json_value) or 'o' (NtvSingle / obj_value)
 300        - 'N' if ntv_name is present else none
 301        - 'T' if ntv_type is present else none'''
 302        dic = {'NtvList': 'l', 'NtvSingle': 's'}
 303        code = dic[self.__class__.__name__]
 304        if isinstance(self, NtvSingle) and not self.is_json:
 305            code = 'o'
 306        if self.ntv_name:
 307            code += 'N'
 308        if self.ntv_type and self.ntv_type.long_name != 'json':
 309            code += 'T'
 310        return code
 311
 312    @property
 313    def json_name_str(self):
 314        '''return the JSON name of the NTV entity'''
 315        return self.json_name(def_type=self.parent.type_str, string=True)
 316
 317    @property
 318    def max_len(self):
 319        '''return the highest len of Ntv entity included'''
 320        maxi = len(self)
 321        if isinstance(self.ntv_value, (list, set)):
 322            maxi = max(maxi, max(ntv.max_len for ntv in self.ntv_value))
 323        return maxi
 324
 325    @property
 326    def name(self):
 327        '''return the ntv_name of the entity'''
 328        return self.ntv_name
 329
 330    @property
 331    def tree(self):
 332        '''return a tree with included entities (NtvTree object)'''
 333        return NtvTree(self)
 334
 335    @property
 336    def typebase_str(self):
 337        '''return a string with the value of the TypeBase of the entity'''
 338        if not self.ntv_type:
 339            return ''
 340        return self.ntv_type.typebase.long_name
 341
 342    @property
 343    def type_str(self):
 344        '''return a string with the value of the NtvType of the entity'''
 345        if not self.ntv_type:
 346            return ''
 347        return self.ntv_type.long_name
 348
 349    @property
 350    def val(self):
 351        '''return the ntv_value of the entity'''
 352        return self.ntv_value
 353
 354    def alike(self, ntv_value):
 355        ''' return a Ntv entity with same name and type.
 356
 357        *Parameters*
 358
 359        - **ntv_value**: list of ntv values'''
 360        return self.__class__(ntv_value, self.ntv_name, self.ntv_type)
 361
 362    def childs(self, obj=False, nam=False, typ=False):
 363        ''' return a list of child Ntv entities or child data
 364
 365        *parameters*
 366
 367        - **obj**: boolean (default False) - return json-value
 368        - **nam**: boolean (default False) - return name (with or without type)
 369        - **typ**: boolean (default False) - return type (with or without name)
 370        '''
 371        if isinstance(self, NtvSingle):
 372            return []
 373        if not (obj or nam or typ):
 374            return self.val
 375        if obj:
 376            return [ntv.to_obj() for ntv in self.val]
 377        return [(ntv.name if nam else '') + (' - ' if nam and typ else '') +
 378                (ntv.type_str if typ else '') for ntv in self.val]
 379
 380    def expand(self, full=True, entity=True):
 381        '''return a json representation of the triplet (name, type, value)
 382
 383
 384        *Parameters*
 385
 386        - **full**: Boolean (default True) - If False only members with non empty values are present
 387        - **entity**: Boolean (default True) - If True, member with entity name is added
 388        '''
 389        exp = {ENTITY: self.__class__.__name__} if entity else {}
 390        if isinstance(self, NtvList) and full:
 391            return exp | {NAME: self.name, TYPE: self.type_str,
 392                          VALUE: [ntv.expand(full) for ntv in self.val]}
 393        if isinstance(self, NtvSingle) and full:
 394            return exp | {NAME: self.name, TYPE: self.type_str, VALUE: self.val}
 395        exp |= {} if not self.name else {NAME: self.name}
 396        if self.type_str not in ['json', '']:
 397            exp[TYPE] = self.type_str
 398        if isinstance(self, NtvList):
 399            exp[VALUE] = [ntv.expand(full) for ntv in self.val]
 400        else:
 401            exp[VALUE] = self.val
 402        return exp
 403
 404    def from_value(self):
 405        '''return a Ntv entity from ntv_value'''
 406        if isinstance(self.ntv_value, list):
 407            return NtvList(self.ntv_value)
 408        return Ntv.from_obj(self.ntv_value)
 409
 410    def json_name(self, def_type=None, string=False, explicit=False):
 411        '''return the JSON name of the NTV entity (json-ntv format)
 412
 413        *Parameters*
 414
 415        - **def_typ** : Datatype or Namespace (default None) - type of the parent entity
 416        - **string** : boolean (default False) - If True, return a string else a tuple
 417        - **explicit** : boolean (default False) - If True, type is always included'''
 418        if def_type is None:
 419            def_type = ''
 420        elif isinstance(def_type, (Datatype, Namespace)):
 421            def_type = def_type.long_name
 422        json_name = self.ntv_name if self.ntv_name else ''
 423        json_type = relative_type(
 424            def_type, self.type_str) if self.ntv_type else ''
 425        implicit = isinstance(self, NtvSingle) and (
 426            json_type == 'json' and (
 427                not def_type or def_type == 'json' or def_type[-1] == '.')
 428            or not NtvConnector.is_json_class(self.val))
 429        if implicit and not explicit:
 430            json_type = ''
 431        json_sep = self._obj_sep(json_name, json_type, def_type)
 432        if string:
 433            return json_name + json_sep + json_type
 434        return [json_name, json_sep, json_type]
 435
 436    @staticmethod
 437    def lower(val1, val2):
 438        ''' compare two ntv_value and return True if val1 < val2, False if val1 > val2 and
 439        None in the other cases'''
 440        res = None
 441        for v1, v2 in zip(Ntv.obj(val1).tree.leaf_nodes, Ntv.obj(val2).tree.leaf_nodes):
 442            if v1.val is None:
 443                res = True
 444            elif isinstance(v1.val, (dict, list)):
 445                res = Ntv.lower(v1.val, v2)
 446            elif isinstance(v2.val, (dict, list)):
 447                res = Ntv.lower(v1, v2.val)
 448            elif isinstance(v1.val, Number):
 449                if isinstance(v2.val, Number):
 450                    res = None if v1.val == v2.val else v1.val < v2.val
 451                else:
 452                    res = False
 453            elif isinstance(v1.val, str):
 454                if isinstance(v2.val, Number):
 455                    res = True
 456                elif isinstance(v2.val, str):
 457                    res = None if v1.val == v2.val else v1.val < v2.val
 458                else:
 459                    res = False
 460            if not res is None:
 461                break
 462        return res
 463
 464    def no_type(self):
 465        '''convert NTV entity in a NV entity (in which ntv_type is 'json' or None')'''
 466        no_typ = copy.copy(self)
 467        for ntv in NtvTree(no_typ).leaf_nodes:
 468            ntv.set_type('json')
 469        for ntv in NtvTree(no_typ).inner_nodes:
 470            ntv.set_type()
 471        return no_typ
 472
 473    def no_name(self):
 474        '''convert NTV entity in a TV entity (in which ntv_name is None)'''
 475        no_nam = copy.copy(self)
 476        for ntv in NtvTree(no_nam):
 477            ntv.ntv_name = None
 478        return no_nam
 479
 480    def no_value(self):
 481        '''convert NTV entity in a NV entity (in which ntv_value of leaf nodes is ntv_type )'''
 482        no_val = copy.copy(self)
 483        for ntv in NtvTree(no_val).leaf_nodes:
 484            ntv.ntv_value = ntv.type_str
 485            ntv.set_type('json')
 486        return no_val
 487
 488    def only_type(self):
 489        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
 490        is ntv_type )'''
 491        only_typ = copy.copy(self)
 492        for ntv in NtvTree(only_typ).leaf_nodes:
 493            ntv.ntv_value = ntv.type_str
 494            ntv.set_type('json')
 495            ntv.set_name()
 496        for ntv in NtvTree(only_typ).inner_nodes:
 497            ntv.set_name()
 498            ntv.set_type()
 499        return only_typ
 500
 501    def only_name(self):
 502        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
 503        is ntv_name )'''
 504        only_nam = copy.copy(self)
 505        for ntv in NtvTree(only_nam).leaf_nodes:
 506            ntv.ntv_value = ntv.name
 507            ntv.set_type('json')
 508            ntv.set_name()
 509        for ntv in NtvTree(only_nam).inner_nodes:
 510            ntv.set_type()
 511        return only_nam
 512
 513    def only_value(self):
 514        '''convert NTV entity in a V entity'''
 515        only_val = copy.copy(self)
 516        for ntv in NtvTree(only_val).leaf_nodes:
 517            ntv.set_type('json')
 518            ntv.set_name()
 519        for ntv in NtvTree(only_val).inner_nodes:
 520            ntv.set_type()
 521            ntv.set_name()
 522        return only_val
 523
 524    @staticmethod
 525    def obj_ntv(value, name='', typ='', single=False):
 526        '''return a json-ntv representation without using Ntv structure.
 527
 528        *Parameters*
 529
 530        - **value** : ntv-value of the json-ntv
 531        - **name** : string (default '') - ntv-name of the json-ntv
 532        - **typ** : string (default '') - ntv_type of the json-ntv
 533        - **single** : boolean (default False) - if True, value is a single object
 534        else value is a set of objetcs.
 535        '''
 536        value = {} if not value else value
 537        name = '' if not name else name
 538        typ = '' if not typ else typ
 539        ntv_list = isinstance(value, (list, dict))
 540        if not single and not ntv_list:
 541            raise NtvError('value is not compatible with not single NTV data')
 542        sep = ':' if single else '::'
 543        sep = '' if not typ and (
 544            not single or single and not ntv_list) else sep
 545        name += sep + typ
 546        value = [value] if not name and isinstance(
 547            value, dict) and len(value) == 1 else value
 548        return {name: value} if name else value
 549
 550    def pointer(self, index=False, item_idx=None):
 551        '''return a nested list of pointer from root
 552
 553        *Parameters*
 554
 555        - **index**: Boolean (default False) - use index instead of name
 556        - **item_idx**: Integer (default None) - index value for the pointer
 557        (useful with duplicate data)'''
 558        if not self.parent:
 559            root_pointer = 0 if index else self.json_name(string=True)
 560            return NtvPointer([root_pointer])
 561        idx = item_idx if item_idx else self.parent.ntv_value.index(self)
 562        num = index or self.parent.json_array
 563        pointer = self.parent.pointer(index)
 564        pointer.append(idx if num else self.json_name_str)
 565        return pointer
 566
 567    def reduce(self, obj=True, maxi=6, level=3):
 568        '''reduce the length and the level of the entity
 569
 570        *Parameters*
 571
 572        - **obj**: boolean (default True) - If True return jsonNTV else NTV entity
 573        - **maxi**: integer (default 6) - Number of returned entities in an NtvList
 574        - **level**: integer (default 6) - returned entities in an NtvList at this level is None
 575
 576        *return*
 577
 578        - **NTV entity** or **jsonNTV**
 579        '''
 580        ntv = copy.copy(self)
 581        cont = Ntv.obj('___') if self.json_array else Ntv.obj({'___': ''})
 582        if isinstance(self, NtvSingle):
 583            return ntv
 584        if level == 0:
 585            ntv.ntv_value = [Ntv.obj('___', no_typ=True)]
 586        if len(self) <= maxi:
 587            ntv.ntv_value = [child.reduce(False, maxi, level-1)
 588                             for child in ntv]
 589            return ntv
 590        mid = maxi // 2
 591        cont.set_type(ntv.type_str)
 592        start = [child.reduce(False, maxi, level-1) for child in ntv[:mid]]
 593        middle = [cont]
 594        end = [child.reduce(False, maxi, level-1) for child in ntv[-mid:]]
 595        ntv.ntv_value = start + middle + end
 596        if obj:
 597            return ntv.to_obj()
 598        return ntv
 599
 600    def remove(self, first=True, index=None):
 601        '''remove self from its parent entity.
 602
 603        *parameters*
 604
 605        - **first** : boolean (default True) - if True only the first instance
 606        else all
 607        - **index** : integer (default None) - index of self in its parent
 608        '''
 609        parent = self.parent
 610        if not parent:
 611            return
 612        idx = parent.ntv_value.index(self) if index is None else index
 613        if not parent[idx] == self:
 614            raise NtvError('the entity is not present at the index')
 615        del parent.ntv_value[idx]
 616        if not first and index is None:
 617            while self in parent:
 618                idx = parent.ntv_value.index(self)
 619                del parent.ntv_value[idx]
 620        if not self in parent:
 621            self.parent = None
 622        return
 623
 624    def replace(self, ntv):
 625        '''replace self by ntv in the tree'''
 626        parent = self.parent
 627        if parent:
 628            idx = parent.ntv_value.index(self)
 629            parent.insert(idx, ntv)
 630            del parent[idx+1]
 631            if not self in parent:
 632                self.parent = None
 633        else:
 634            raise NtvError('replace is not available for root node')
 635
 636    def set_name(self, name='', nodes='simple'):
 637        '''set new names to the entity
 638
 639        *Parameters*
 640
 641        - **name**: list or string (default '') - New name values
 642        - **nodes**: string (default 'simple') - nodes to be changed
 643            'simple': current entity
 644            'leaves': NtvSingle entities
 645            'inner': NtvList entities
 646            'all': all entities  '''
 647        name = '' if name is None else name
 648        match nodes:
 649            case 'simple':
 650                self.ntv_name = str(name)
 651            case 'leaves':
 652                if not isinstance(name, list):
 653                    name = [str(name)] * NtvTree(self).breadth
 654                for nam, ntv in zip(name, NtvTree(self).leaf_nodes):
 655                    ntv.ntv_name = nam
 656            case 'inner':
 657                if not isinstance(name, list):
 658                    name = [str(name)] * len(NtvTree(self).inner_nodes)
 659                for nam, ntv in zip(name, NtvTree(self).inner_nodes):
 660                    ntv.ntv_name = nam
 661            case 'all':
 662                if not isinstance(name, list):
 663                    name = [str(name)] * NtvTree(self).size
 664                for nam, ntv in zip(name, NtvTree(self).nodes):
 665                    ntv.ntv_name = nam
 666            case _:
 667                raise NtvError('the nodes option is not valid')
 668
 669    def set_type(self, typ=None):
 670        '''set a new type to the entity
 671
 672        *Parameters*
 673
 674        - **typ**: string, Datatype, Namespace (default None)'''
 675        if typ and not isinstance(typ, (str, Datatype, Namespace)):
 676            raise NtvError('the type is not a valid type')
 677        self.ntv_type = str_type(typ, self.__class__.__name__ == 'NtvSingle')
 678
 679    def set_value(self, value=None, fast=False):
 680        '''set new ntv_value of a single entity or of a list of entities included
 681
 682        *Parameters*
 683
 684        - **value**: list or single value
 685        - **fast** : boolean (default False) - if True, value is not converted'''
 686        if isinstance(self, NtvSingle):
 687            self.ntv_value = NtvSingle(value, ntv_type=self.ntv_type,
 688                                       fast=fast).val
 689            return
 690        if not isinstance(value, list):
 691            value = [value] * NtvTree(self).breadth
 692        ntv_val = NtvList(value, fast=fast)
 693        for val, ntv in zip(ntv_val, NtvTree(self).leaf_nodes):
 694            ntv.ntv_value = val.val
 695        return
 696
 697    def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
 698        '''convert NtvList entity to NtvSingle entity
 699
 700        *Parameters*
 701
 702        - **ntv_name** : String (default None) - name of the NTV entity
 703        - **ntv_type**: String (default None) - type of the entity
 704        - **value**: value of the entity
 705        - **fast**: boolean (default False) - Ntv is created with a list of json values
 706        without control
 707        '''
 708        return NtvSingle(self.obj_value(def_type=def_type, **kwargs),
 709                         self.name if self.name else name,
 710                         self.type_str if self.type_str else typ)
 711
 712    def to_ntvlist(self, def_type=None, def_sep=None, no_typ=False, decode_str=False,
 713                   typ_auto=False, fast=False):
 714        '''convert NtvSingle entity to NtvList entity
 715
 716        *Parameters*
 717
 718        - **value**: Ntv value to convert in an Ntv entity
 719        - **no_typ** : boolean (default None) - if True, NtvList is with 'json' type
 720        - **def_type** : Datatype or Namespace (default None) - default type of the value
 721        - **def_sep**: ':', '::' or None (default None) - default separator of the value
 722        - **decode_str**: boolean (default False) - if True, string are loaded as json data
 723        - **type_auto**: boolean (default False) - if True, default type for NtvList
 724        is the ntv_type of the first Ntv in the ntv_value
 725        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
 726        '''
 727        ntv = Ntv.from_obj(self.ntv_value, def_type, def_sep, no_typ, decode_str,
 728                           typ_auto, fast)
 729        if ntv.__class__.__name__ == 'NtvSingle':
 730            return NtvList([self])
 731        if self.ntv_name:
 732            ntv.set_name(self.ntv_name)
 733        return ntv
 734
 735    def to_mermaid(self, title='', disp=False, row=False, leaves=False):
 736        '''return a mermaid flowchart.
 737
 738        *Parameters*
 739
 740        - **title**: String (default '') - title of the flowchart
 741        - **disp**: Boolean (default False) - if true, return a display else return
 742        a mermaid text diagram
 743        - **row**: Boolean (default False) - if True, add the node row
 744        - **leaves**: Boolean (default False) - if True, add the leaf row
 745        '''
 746        option = {'title': title, 'disp': disp, 'row': row, 'leaves': leaves}
 747        if disp:
 748            Ntv.obj({':$mermaid': self.to_obj()}).to_obj(
 749                format='obj', **option)
 750            return None
 751        return Ntv.obj({':$mermaid': self.to_obj()}).to_obj(format='obj', **option)
 752
 753    def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
 754        '''return a simple json representation of the Ntv entity.
 755
 756        *Parameters*
 757
 758        - **nam**: Boolean(default True) - if true, the names are included
 759        - **typ**: Boolean(default True) - if true, the types are included
 760        - **val**: Boolean(default True) - if true, the values are included
 761        - **jsn**: Boolean(default False) - if false, the 'json' type is not included
 762        - **maxi**: Integer (default 10) - number of values to include for NtvList
 763        entities. If maxi < 1 all the values are included.
 764        '''
 765        ntv = self.code_ntv
 766        if nam and typ:
 767            ntv = ntv[0]
 768        if self.ntv_name and nam:
 769            ntv += '-' + self.ntv_name
 770        if self.ntv_type and typ and (jsn or self.ntv_type.long_name != 'json'):
 771            ntv += '-' + self.ntv_type.long_name
 772        clas = self.__class__.__name__
 773        clas_val = self.ntv_value.__class__.__name__
 774        if clas == 'NtvSingle' and clas_val != 'NtvSingle':
 775            if val:
 776                if ntv:
 777                    ntv += '-'
 778                ntv += json.dumps(self.ntv_value, cls=NtvJsonEncoder)
 779            return ntv
 780        if clas == 'NtvSingle' and clas_val == 'NtvSingle':
 781            return {ntv:  self.ntv_value.to_repr(nam, typ, val, jsn, maxi)}
 782        if clas == 'NtvList':
 783            maxv = len(self.ntv_value) if maxi < 1 else maxi
 784            return {ntv:  [ntvi.to_repr(nam, typ, val, jsn, maxi)
 785                           for ntvi in self.ntv_value[:maxv]]}
 786        raise NtvError('the ntv entity is not consistent')
 787
 788    def to_name(self, default=''):
 789        '''return the name of the NTV entity
 790
 791        *Parameters*
 792
 793        - **default**: string (default ''): returned value if name is not present '''
 794        if self.ntv_name == '':
 795            return default
 796        return self.ntv_name
 797
 798    def to_fast(self, def_type=None, **kwargs):
 799        '''return the JSON representation of the NTV entity (json-ntv format)
 800        without value conversion.
 801
 802        *Parameters*
 803
 804        - **def_type** : Datatype or Namespace (default None) - default type to apply
 805        to the NTV entity
 806        - **encoded** : boolean (default False) - choice for return format
 807        (string/bytes if True, dict/list/tuple else)
 808        - **format**  : string (default 'json')- choice for return format
 809        (json, cbor, obj)
 810        - **simpleval** : boolean (default False) - if True, only value (without
 811        name and type) is included
 812        - **name** : boolean (default true) - if False, name is not included
 813        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
 814        - **maxi**: Integer (default -1) - number of values to include for NtvList
 815        entities. If maxi < 1 all the values are included.
 816        '''
 817        option = kwargs | {'fast': True}
 818        return self.to_obj(def_type=def_type, **option)
 819
 820    def to_obj(self, def_type=None, **kwargs):
 821        '''return the JSON representation of the NTV entity (json-ntv format).
 822
 823        *Parameters*
 824
 825        - **def_type** : Datatype or Namespace (default None) - default type to apply
 826        to the NTV entity
 827        - **encoded** : boolean (default False) - choice for return format
 828        (string/bytes if True, dict/list/tuple else)
 829        - **format**  : string (default 'json')- choice for return format
 830        (json, cbor, obj)
 831        - **simpleval** : boolean (default False) - if True, only value (without
 832        name and type) is included
 833        - **name** : boolean (default true) - if False, name is not included
 834        - **type** : boolean (default False) - if True, type is always included
 835        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
 836        - **fast** : boolean (default False) - if True, json is created without conversion
 837        - **maxi**: Integer (default -1) - number of values to include for NtvList
 838        entities. If maxi < 1 all the values are included.
 839        '''
 840        option = {'encoded': False, 'format': 'json', 'fast': False, 'maxi': -1,
 841                  'simpleval': False, 'name': True, 'json_array': False,
 842                  'type': False} | kwargs
 843        value = self.obj_value(def_type=def_type, **option)
 844        obj_name = self.json_name(def_type)
 845        if not option['name']:
 846            obj_name[0] = ''
 847        if option['simpleval']:
 848            name = ''
 849        elif (option['format'] in ('cbor', 'obj') and
 850              not NtvConnector.is_json_class(value) and not option['type']):
 851            name = obj_name[0]
 852        else:
 853            name = obj_name[0] + obj_name[1] + obj_name[2]
 854        value = [value] if not name and isinstance(
 855            value, dict) and len(value) == 1 else value
 856        json_obj = {name: value} if name else value
 857        if option['encoded'] and option['format'] == 'json':
 858            return json.dumps(json_obj, cls=NtvJsonEncoder)
 859        if option['encoded'] and option['format'] == 'cbor':
 860            return NtvConnector.connector()['CborConnec'].to_obj_ntv(json_obj)
 861        return json_obj
 862
 863    def to_json_ntv(self):
 864        ''' create a copy where ntv-value of the self-tree nodes is converted
 865        in json-value'''
 866        ntv = copy.copy(self)
 867        for leaf in ntv.tree.leaf_nodes:
 868            if isinstance(leaf.ntv_value, (NtvSingle, NtvList)):
 869                leaf.ntv_value = leaf.ntv_value.to_obj()
 870                leaf.ntv_type = Datatype('ntv')
 871            elif not leaf.is_json:
 872                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.cast(
 873                    leaf.ntv_value, leaf.ntv_name, leaf.type_str)
 874                leaf.ntv_type = Datatype(type_str)
 875                leaf.is_json = True
 876        return ntv
 877
 878    def to_obj_ntv(self, **kwargs):
 879        ''' create a copy where ntv-value of the self-tree nodes is converted
 880        in object-value
 881
 882        *Parameters*
 883
 884        - **kwargs** : parameters used in NtvConnector class (specific for each Connector)'''
 885        ntv = copy.copy(self)
 886        for leaf in ntv.tree.leaf_nodes:
 887            if (leaf.is_json and leaf.type_str in set(NtvConnector.dic_type.values())
 888                    or leaf.ntv_type is None):
 889                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.uncast(
 890                    leaf, **kwargs)
 891                leaf.ntv_type = Datatype(type_str) if type_str else None
 892                leaf.is_json = NtvConnector.is_json(leaf.ntv_value)
 893        return ntv
 894
 895    def to_tuple(self, maxi=10):
 896        '''return a nested tuple representation of the NTV entity
 897        (NtvList/NtvSingle, ntv_name, ntv_type, ntv_value).
 898
 899        *Parameters*
 900
 901        - **maxi**: Integer (default 10) - number of values to include for NtvList
 902        entities. If maxi < 1 all the values are included.
 903        '''
 904        clas = self.__class__.__name__
 905        val = self.ntv_value
 906        name = self.ntv_name
 907        typ = None
 908        if self.ntv_type:
 909            typ = self.ntv_type.long_name
 910        if isinstance(self, NtvSingle) and not isinstance(val, NtvSingle):
 911            return (clas, name, typ, val)
 912        if isinstance(self, NtvSingle) and isinstance(val, NtvSingle):
 913            return (clas, name, typ, val.to_tuple(maxi=maxi))
 914        if isinstance(self, NtvList):
 915            maxv = len(self.ntv_value) if maxi < 1 else maxi
 916            return (clas, name, typ, [ntv.to_tuple(maxi=maxi) for ntv in val[:maxv]])
 917        raise NtvError('the ntv entity is not consistent')
 918
 919    def validate(self, unique=False):
 920        '''check the ntv_type validity and return a boolean and a list of errors
 921
 922        *Parameters*
 923
 924        - **unique**: boolean (default False) - if True, stop validation at the
 925        first error'''
 926        errors = []
 927        for ntv in self.tree.leaf_nodes:
 928            valid = ntv.ntv_type.validate(ntv.ntv_value)
 929            if not valid:
 930                errors.append(str(ntv.pointer()))
 931                if unique:
 932                    return (False, errors)
 933        return (not errors, errors)
 934
 935    @abstractmethod
 936    def obj_value(self, def_type=None, **kwargs):
 937        '''return the ntv_value with different formats defined by kwargs (abstract method)'''
 938
 939    @property
 940    @abstractmethod
 941    def json_array(self):
 942        ''' return the json_array dynamic attribute (abstract method)'''
 943
 944    @abstractmethod
 945    def _obj_sep(self, json_name, json_type, def_type=None):
 946        ''' return separator to include in json_name (abstract method)'''
 947
 948    @staticmethod
 949    def _from_value(value, decode_str=False):
 950        '''return a decoded value
 951
 952        *Parameters*
 953
 954        - **decode_str**: boolean (default False) - if True, string are loaded as json data
 955        '''
 956        if isinstance(value, bytes):
 957            value = Ntv.from_obj({'$cbor': value}).ntv_value
 958        elif decode_str and isinstance(value, str) and value.lstrip() and\
 959                value.lstrip()[0] in '"-{[0123456789tfn':
 960            try:
 961                value = json.loads(value)
 962            except json.JSONDecodeError:
 963                pass
 964        return value
 965
 966    @staticmethod
 967    def decode_json(json_value):
 968        '''return (value, name, type, separator, isjson) of a json object'''
 969        if isinstance(json_value, dict) and len(json_value) == 1:
 970            json_name = list(json_value.keys())[0]
 971            val = json_value[json_name]
 972            return (val, *NtvUtil.from_obj_name(json_name), NtvConnector.is_json(val))
 973        return (json_value, None, None, None, NtvConnector.is_json(json_value))
 974
 975    @staticmethod
 976    def _create_ntvlist(str_typ, def_type, sep, ntv_value, typ_auto, no_typ, ntv_name, fast):
 977        '''return a NtvList with parameters from Ntv.from_obj method'''
 978        def_type = agreg_type(str_typ, def_type, False)
 979        sep_val = ':' if sep and def_type else None
 980        if isinstance(ntv_value, dict):
 981            keys = list(ntv_value.keys())
 982            values = list(ntv_value.values())
 983            ntv_list = [Ntv.from_obj({key: val}, def_type, sep_val, fast=fast)
 984                        for key, val in zip(keys, values)]
 985        else:
 986            ntv_list = [Ntv.from_obj(val, def_type, sep_val, fast=fast)
 987                        for val in ntv_value]
 988        if typ_auto and not def_type and ntv_list:
 989            def_type = ntv_list[0].ntv_type
 990        def_type = None if no_typ else def_type
 991        return NtvList(ntv_list, ntv_name, def_type, typ_auto, fast=fast)
 992
 993    @staticmethod
 994    def _listed(idx):
 995        '''transform a tuple of tuple object in a list of list object'''
 996        return [val if not isinstance(val, tuple) else Ntv._listed(val) for val in idx]
 997
 998
 999class NtvSingle(Ntv):
1000    ''' A NTV-single entity is a Ntv entity not composed with other entities.
1001
1002    *Attributes :*
1003    - no additional attributes to those of parent class `Ntv`
1004
1005    *dynamic values (@property)*
1006    - `json_array`
1007
1008    The additional methods defined in this class are :
1009
1010    *instance methods*
1011    - `obj_value`
1012    '''
1013
1014    def __init__(self, value, ntv_name=None, ntv_type=None, fast=False):
1015        '''NtvSingle constructor.
1016
1017        *Parameters*
1018
1019        - **ntv_name** : String (default None) - name of the NTV entity
1020        - **ntv_type**: String (default None) - type of the entity
1021        - **value**: value of the entity
1022        - **fast**: boolean (default False) - Ntv is created with a list of json values
1023        without control
1024        '''
1025        if not fast:
1026            value, ntv_name, ntv_type = NtvSingle._decode_s(
1027                value, ntv_name, ntv_type)
1028            if ntv_type and isinstance(ntv_type, str) and ntv_type[-1] == '.':
1029                raise NtvError('the ntv_type is not valid')
1030        super().__init__(value, ntv_name, ntv_type)
1031
1032    def __eq__(self, other):
1033        ''' equal if name type and value are equal'''
1034        return self.__class__.__name__ == other.__class__.__name__ and\
1035            self.ntv_name == other.ntv_name and self.ntv_type == other.ntv_type and\
1036            self.ntv_value == other.ntv_value
1037
1038    def __hash__(self):
1039        '''return hash(name) + hash(type) + hash(value)'''
1040        return hash(self.ntv_name) + hash(self.ntv_type) + \
1041            hash(json.dumps(self.ntv_value, cls=NtvJsonEncoder))
1042
1043    def __copy__(self):
1044        ''' Copy all the Ntv tree '''
1045        return self.__class__(copy.copy(self.ntv_value), self.ntv_name,
1046                              self.ntv_type, fast=True)
1047
1048    @property
1049    def json_array(self):
1050        ''' return the json_array dynamic attribute (always False)'''
1051        return False
1052
1053    def obj_value(self, def_type=None, **kwargs):
1054        '''return the ntv_value with different formats defined by kwargs'''
1055        option = {'encoded': False, 'format': 'json',
1056                  'simpleval': False, 'fast': False} | kwargs
1057        if option['fast'] or option['format'] in ('json', 'tuple'):
1058            return self.ntv_value
1059        if option['format'] == 'obj' and self.ntv_value == 'null':
1060            return None
1061        return NtvConnector.uncast(self, **option)[0]
1062
1063    def _obj_sep(self, json_name, json_type, def_type=None):  # REQ5
1064        ''' return separator to include in json_name'''
1065        if json_type or not def_type and isinstance(self.ntv_value, (list, dict)):
1066            return ':'
1067        return ''
1068
1069    @staticmethod
1070    def _decode_s(ntv_value, ntv_name, ntv_type_str):
1071        '''return adjusted ntv_value, ntv_name, ntv_type(str)'''
1072        is_json = NtvConnector.is_json(ntv_value)
1073        name = None
1074        if is_json:
1075            if isinstance(ntv_value, (list)):
1076                ntv_value = [NtvSingle._decode_s(val, '', ntv_type_str)[
1077                    0] for val in ntv_value]
1078            elif isinstance(ntv_value, (dict)):
1079                ntv_value = {key: NtvSingle._decode_s(val, '', ntv_type_str)[
1080                    0] for key, val in ntv_value.items()}
1081        elif isinstance(ntv_value, NtvSingle):
1082            ntv_value = ntv_value.to_obj()
1083            return (ntv_value, ntv_name, 'ntv')
1084        else:
1085            ntv_value, name, typ_str = NtvConnector.cast(ntv_value, ntv_name)
1086            ntv_type_str = Datatype(typ_str).name if typ_str else ntv_type_str
1087        if not ntv_type_str:
1088            if is_json:
1089                ntv_type_str = 'json'
1090            else:
1091                ntv_type_str = typ_str
1092        elif not is_json and ntv_type_str != typ_str:
1093            raise NtvError('ntv_value is not compatible with ntv_type')
1094        ntv_name = name if not ntv_name else ntv_name
1095        return (ntv_value, ntv_name, ntv_type_str)
1096
1097
1098class NtvList(Ntv):
1099    '''A NTV-list entity is a Ntv entity where:
1100
1101    - ntv_value is a list of NTV entities,
1102    - ntv_type is a default type available for included NTV entities
1103
1104    *Attributes :*
1105    - no additional attributes to those of parent class `Ntv`
1106
1107    *dynamic values (@property)*
1108    - `json_array`
1109
1110    The additional methods defined in this class are :
1111
1112    *instance methods*
1113    - `obj_value`
1114    '''
1115
1116    def __init__(self, list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False):
1117        '''NtvList constructor.
1118
1119        *Parameters*
1120
1121        - **ntv_name** : String (default None) - name of the NTV entity
1122        - **ntv_type**: String (default None) - default type or namespace of
1123        the included entities
1124        - **list_ntv**: list - list of Ntv objects or obj_value of Ntv objects
1125        - **fast**: boolean (default False) - if True, Ntv is created with a list
1126        of json values without control
1127        - **type_auto**: boolean (default False) - if True, default type for NtvList
1128        is the ntv_type of the first Ntv in the ntv_value'''
1129        if isinstance(list_ntv, NtvList):
1130            ntv_value = [copy.copy(ntv) for ntv in list_ntv.ntv_value]
1131            ntv_type = list_ntv.ntv_type
1132            ntv_name = list_ntv.ntv_name
1133        elif isinstance(list_ntv, list):
1134            ntv_value = [Ntv.from_obj(ntv, ntv_type, ':', fast=fast)
1135                         for ntv in list_ntv]
1136        elif isinstance(list_ntv, dict):
1137            ntv_value = [Ntv.from_obj({key: val}, ntv_type, ':', fast=fast)
1138                         for key, val in list_ntv.items()]
1139        else:
1140            raise NtvError('ntv_value is not a list')
1141        if typ_auto and not ntv_type and len(ntv_value) > 0 and ntv_value[0].ntv_type:
1142            ntv_type = ntv_value[0].ntv_type
1143        super().__init__(ntv_value, ntv_name, ntv_type)
1144        for ntv in self:
1145            ntv.parent = self
1146
1147    @property
1148    def json_array(self):
1149        ''' return the json_array dynamic attribute'''
1150        set_name = {ntv.json_name(
1151            def_type=self.type_str, string=True) for ntv in self}
1152        return '' in set_name or len(set_name) != len(self)
1153
1154    def __eq__(self, other):
1155        ''' equal if name and value are equal'''
1156        return self.__class__.__name__ == other.__class__.__name__ and\
1157            self.ntv_name == other.ntv_name and self.ntv_value == other.ntv_value
1158
1159    def __hash__(self):
1160        '''return hash(name) + hash(value)'''
1161        return hash(self.ntv_name) + hash(tuple(self.ntv_value))
1162
1163    def __copy__(self):
1164        ''' Copy all the data '''
1165        cop = self.__class__(self)
1166        cop.parent = None
1167        return cop
1168
1169    def __setitem__(self, ind, value):
1170        ''' replace ntv_value item at the `ind` row with `value`'''
1171        if ind < 0 or ind >= len(self):
1172            raise NtvError("out of bounds")
1173        self.ntv_value[ind] = value
1174        if isinstance(value, (NtvSingle, NtvList)):
1175            value.parent = self
1176
1177    def __delitem__(self, ind):
1178        '''remove ntv_value item at the `ind` row'''
1179        if isinstance(ind, int):
1180            self.ntv_value.pop(ind)
1181        else:
1182            self.ntv_value.pop(self.ntv_value.index(self[ind]))
1183
1184    def append(self, ntv):
1185        ''' add ntv at the end of the list of Ntv entities included'''
1186        old_parent = ntv.parent
1187        if old_parent:
1188            del old_parent[old_parent.ntv_value.index(ntv)]
1189        self.ntv_value.append(ntv)
1190        ntv.parent = self
1191
1192    def insert(self, idx, ntv):
1193        ''' add ntv at the index idx of the list of Ntv entities included'''
1194        old_parent = ntv.parent
1195        if old_parent:
1196            del old_parent[old_parent.ntv_value.index(ntv)]
1197        self.ntv_value.insert(idx, ntv)
1198        ntv.parent = self
1199
1200    def _obj_sep(self, json_name, json_type, def_type=None):
1201        ''' return separator to include in json_name'''
1202        sep = ':' if (json_type and json_type[-1] == '.') else '::'
1203        return sep if (json_type and json_type[-1] != '.') or (json_type and json_name) else ''
1204
1205    def obj_value(self, def_type=None, **kwargs):
1206        '''return the ntv_value with different formats defined by kwargs
1207        '''
1208        option = {'encoded': False, 'format': 'json', 'simpleval': False,
1209                  'json_array': False, 'fast': False, 'maxi': -1} | kwargs
1210        opt2 = option | {'encoded': False}
1211        maxv = len(self.ntv_value) if option['maxi'] < 1 else option['maxi']
1212        def_type = self.ntv_type.long_name if self.ntv_type else def_type
1213        values = [ntv.to_obj(def_type=def_type, **opt2)
1214                  for ntv in self.ntv_value[:maxv]]
1215        if len(self) == 1 and isinstance(self[0], NtvSingle) and isinstance(values[0], dict):
1216            return values[0]
1217        if not NtvUtil.is_dictable(values) or option['simpleval'] or option['json_array']:
1218            return values
1219        return {list(val.items())[0][0]: list(val.items())[0][1] for val in values}
NAME = 'N'
TYPE = 'T'
VALUE = 'V'
ENTITY = 'E'
NTVSINGLE = 'S'
NTVLIST = 'L'
class Ntv(abc.ABC, json_ntv.ntv_util.NtvUtil):
 33class Ntv(ABC, NtvUtil):
 34    ''' The Ntv class is an abstract class used by `NtvSingle`and `NtvList` classes.
 35
 36    *Attributes :*
 37    - **ntv_name** :  String - name of the NTV entity
 38    - **ntv_type**:   Datatype or Namespace - type of the entity
 39    - **ntv_value**:  value of the entity
 40
 41    *Internal attributes :*
 42    - **parent**:     parent NtvList entity
 43    - **is_json**:    True if ntv_value is a json_value
 44
 45    *dynamic values (@property)*
 46    - `code_ntv`
 47    - `json_name_str`
 48    - `json_array` (abstract method)
 49    - `max_len`
 50    - `name`
 51    - `tree`
 52    - `type_str`
 53    - `val`
 54
 55    The methods defined in this class are :
 56
 57    *Ntv constructor (staticmethod)*
 58    - `fast`
 59    - `obj`
 60    - `from_obj`
 61    - `from_att`
 62
 63    *NTV conversion (instance methods)*
 64    - `alike`
 65    - `to_json_ntv`
 66    - `to_obj_ntv`
 67    - `validate` (validation)
 68
 69    *export - conversion (instance methods)*
 70    - `expand`
 71    - `no_type`
 72    - `no_name`
 73    - `reduce`
 74    - `to_fast`
 75    - `to_name`
 76    - `to_obj`
 77    - `to_repr`
 78    - `to_mermaid`
 79    - `to_tuple`
 80    - `to_ntvsingle`
 81    - `to_ntvlist`
 82
 83    *tree methods (instance methods)*
 84    - `childs`
 85    - `pointer`
 86    - `replace`
 87    - `remove`
 88    - `append` (NtvList only)
 89    - `insert` (NtvList only)
 90
 91    *other instance methods*
 92    - `from_value`
 93    - `json_name`
 94    - `set_name`
 95    - `set_type`
 96    - `set_value`
 97    - `obj_value` (abstract method)
 98
 99    *utility methods*
100    - `decode_json` *(staticmethod)*
101    - `obj_ntv` *(staticmethod)*
102    '''
103
104    def __init__(self, ntv_value, ntv_name, ntv_type):
105        '''Ntv constructor.
106
107        *Parameters*
108
109        - **ntv_value**: Json entity - value of the entity
110        - **ntv_name** : String (default None) - name of the NTV entity
111        - **ntv_type**: String or Datatype or Namespace (default None) - type of the entity
112        '''
113        if ntv_type.__class__.__name__ in ['Datatype', 'Namespace']:
114            self.ntv_type = ntv_type
115        elif ntv_type and ntv_type[-1] != '.':
116            self.ntv_type = Datatype(ntv_type)
117        elif ntv_type and ntv_type[-1] == '.':
118            self.ntv_type = Namespace.add(ntv_type)
119        else:
120            self.ntv_type = None
121        if not isinstance(ntv_name, str):
122            ntv_name = ''
123        self.ntv_name = ntv_name
124        self.ntv_value = ntv_value
125        self.is_json = NtvConnector.is_json(ntv_value)
126        self.parent = None
127
128    @staticmethod
129    def fast(data, no_typ=False, typ_auto=False):
130        ''' return an Ntv entity from data without conversion.
131
132        *Parameters* : see `obj` method'''
133        return Ntv.obj(data, no_typ=no_typ, typ_auto=typ_auto, fast=True)
134
135    @staticmethod
136    def obj(data, no_typ=False, decode_str=False, typ_auto=False, fast=False):
137        ''' return an Ntv entity from data.
138
139        *Parameters*
140
141        - **Data** can be :
142            - a tuple with value, name, typ and cat (see `from_att` method)
143            - a value to decode (see `from_obj`method)
144        - **no_typ** : boolean (default False) - if True, NtvList is with None type
145        - **type_auto**: boolean (default False) - if True, default type for NtvList
146        is the ntv_type of the first Ntv in the ntv_value
147        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
148        - **decode_str**: boolean (default False) - if True, string are loaded in json data'''
149        if isinstance(data, tuple):
150            return Ntv.from_att(*data, decode_str=decode_str, fast=fast)
151        if isinstance(data, str):
152            try:
153                data = json.loads(data)
154            except json.JSONDecodeError:
155                pass
156        return Ntv.from_obj(data, no_typ=no_typ, decode_str=decode_str,
157                            typ_auto=typ_auto, fast=fast)
158
159    @staticmethod
160    def from_att(value, name, typ, cat, decode_str=False, fast=False):
161        ''' return an Ntv entity.
162
163        *Parameters*
164
165        - **value**: Ntv entity or value to convert in an Ntv entity
166        - **name** : string - name of the Ntv entity
167        - **typ** : string or NtvType - type of the NTV entity
168        - **cat**: string - NTV category ('single', 'list')
169        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
170        - **decode_str**: boolean (default False) - if True, string are loaded as json data'''
171
172        value = Ntv._from_value(value, decode_str)
173        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
174            return value
175        if isinstance(value, list) and cat == 'list':
176            return NtvList(value, name, typ, fast=fast)
177        if cat == 'single':
178            return NtvSingle(value, name, typ, fast=fast)
179        return Ntv.from_obj(value, def_type=typ, fast=fast)
180
181    @staticmethod
182    def from_obj(value, def_type=None, def_sep=None, no_typ=False, decode_str=False,
183                 typ_auto=False, fast=False):
184        ''' return an Ntv entity from an object value.
185
186        *Parameters*
187
188        - **value**: Ntv value to convert in an Ntv entity
189        - **no_typ** : boolean (default None) - if True, NtvList is with None type
190        - **def_type** : Datatype or Namespace (default None) - default type of the value
191        - **def_sep**: ':', '::' or None (default None) - default separator of the value
192        - **decode_str**: boolean (default False) - if True, string are loaded as json data
193        - **type_auto**: boolean (default False) - if True, default type for NtvList
194        is the ntv_type of the first Ntv in the ntv_value
195        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion'''
196        value = Ntv._from_value(value, decode_str)
197        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
198            return value
199        ntv_value, ntv_name, str_typ, sep, is_json = Ntv.decode_json(value)
200        sep = def_sep if not sep else sep
201        sep = None if str_typ and str_typ[-1] == '.' and sep == ':' else sep
202        if isinstance(ntv_value, (list, dict)) and sep in (None, '::'):
203            return Ntv._create_ntvlist(str_typ, def_type, sep, ntv_value,
204                                       typ_auto, no_typ, ntv_name, fast)
205        if sep == ':' or (sep is None and isinstance(ntv_value, dict)):
206            ntv_type = agreg_type(str_typ, def_type, False)
207            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
208        if sep is None and not isinstance(ntv_value, dict):
209            is_single_json = isinstance(ntv_value, (int, str, float, bool))
210            ntv_type = agreg_type(str_typ, def_type, is_single_json)
211            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
212        raise NtvError('separator ":" is not compatible with value')
213
214    def __len__(self):
215        ''' len of ntv_value'''
216        if isinstance(self.ntv_value, list):
217            return len(self.ntv_value)
218        return 1
219
220    def __str__(self):
221        '''return string format'''
222        return self.to_obj(encoded=True)
223
224    def __repr__(self):
225        '''return classname and code'''
226        return self.reduce(obj=False, level=3, maxi=6).to_obj(encoded=True)
227
228    def __contains__(self, item):
229        ''' item of Ntv entities'''
230        if isinstance(self.val, list):
231            return item in self.ntv_value
232        return item == self.ntv_value
233
234    def __iter__(self):
235        ''' iterator for Ntv entities'''
236        if isinstance(self, NtvSingle):
237            return iter([self])
238        return iter(self.val)
239
240    def __getitem__(self, selec):
241        ''' return ntv_value item with selec:
242            - String beginning with "#" : json-pointer,
243            - string : name of the ntv,
244            - list : recursive selector
245            - tuple : list of name or index '''
246        if selec is None or selec == '':
247            return self
248        if isinstance(selec, NtvPointer) or isinstance(selec, str) and selec[0] == '#':
249            selec = self._pointer_to_list(selec)
250        if selec in ([], ()):
251            return self
252        if isinstance(selec, (list, tuple)) and len(selec) == 1:
253            selec = selec[0]
254        if selec in (0, self.ntv_name) and isinstance(self, NtvSingle):
255            return self.ntv_value
256        if isinstance(self, NtvSingle):
257            raise NtvError('item not present')
258        if isinstance(selec, tuple):
259            return [self[i] for i in selec]
260        if isinstance(selec, str):
261            return self.ntv_value[self._string_to_ind(selec)]
262        if isinstance(selec, list):
263            return self[selec[0]][selec[1:]]
264        return self.ntv_value[selec]
265
266    def _pointer_to_list(self, pointer):
267        '''return a list of child pointers from a string or a NtvPointer'''
268        if isinstance(pointer, str):
269            selec = list(NtvPointer(pointer[1:]))
270        elif isinstance(pointer, NtvPointer):
271            selec = list(pointer)
272        else:
273            raise NtvError('pointer is not a valid pointer')
274        if not (selec[0] in [self.json_name(string=True), self.ntv_name]
275                or (isinstance(selec[0], int) and selec[0] == 0)):
276            raise NtvError(
277                str(selec[0]) + 'is not the root json_name : ' + self.ntv_name)
278        return selec[1:]
279
280    def _string_to_ind(self, json_name):
281        '''return the index of a name or a json_name'''
282        if NtvUtil.from_obj_name(json_name)[1]:
283            return [ntv.json_name(def_type=self.type_str, string=True)
284                    for ntv in self.ntv_value].index(json_name)
285        return [ntv.ntv_name for ntv in self.ntv_value].index(json_name)
286
287    def __lt__(self, other):
288        ''' return a comparison between two ntv_value'''
289        # order: number > string > None
290        res = Ntv.lower(self, other)
291        if res is None:
292            res = None if len(self) == len(other) else len(self) < len(other)
293        res = self.to_obj(encoded=True) < other.to_obj(
294            encoded=True) if res is None else res
295        return res
296
297    @property
298    def code_ntv(self):
299        '''return a string with the NTV code composed with 1 to 3 letters:
300        - 'l' (NtvList), 's' (NtvSingle / json_value) or 'o' (NtvSingle / obj_value)
301        - 'N' if ntv_name is present else none
302        - 'T' if ntv_type is present else none'''
303        dic = {'NtvList': 'l', 'NtvSingle': 's'}
304        code = dic[self.__class__.__name__]
305        if isinstance(self, NtvSingle) and not self.is_json:
306            code = 'o'
307        if self.ntv_name:
308            code += 'N'
309        if self.ntv_type and self.ntv_type.long_name != 'json':
310            code += 'T'
311        return code
312
313    @property
314    def json_name_str(self):
315        '''return the JSON name of the NTV entity'''
316        return self.json_name(def_type=self.parent.type_str, string=True)
317
318    @property
319    def max_len(self):
320        '''return the highest len of Ntv entity included'''
321        maxi = len(self)
322        if isinstance(self.ntv_value, (list, set)):
323            maxi = max(maxi, max(ntv.max_len for ntv in self.ntv_value))
324        return maxi
325
326    @property
327    def name(self):
328        '''return the ntv_name of the entity'''
329        return self.ntv_name
330
331    @property
332    def tree(self):
333        '''return a tree with included entities (NtvTree object)'''
334        return NtvTree(self)
335
336    @property
337    def typebase_str(self):
338        '''return a string with the value of the TypeBase of the entity'''
339        if not self.ntv_type:
340            return ''
341        return self.ntv_type.typebase.long_name
342
343    @property
344    def type_str(self):
345        '''return a string with the value of the NtvType of the entity'''
346        if not self.ntv_type:
347            return ''
348        return self.ntv_type.long_name
349
350    @property
351    def val(self):
352        '''return the ntv_value of the entity'''
353        return self.ntv_value
354
355    def alike(self, ntv_value):
356        ''' return a Ntv entity with same name and type.
357
358        *Parameters*
359
360        - **ntv_value**: list of ntv values'''
361        return self.__class__(ntv_value, self.ntv_name, self.ntv_type)
362
363    def childs(self, obj=False, nam=False, typ=False):
364        ''' return a list of child Ntv entities or child data
365
366        *parameters*
367
368        - **obj**: boolean (default False) - return json-value
369        - **nam**: boolean (default False) - return name (with or without type)
370        - **typ**: boolean (default False) - return type (with or without name)
371        '''
372        if isinstance(self, NtvSingle):
373            return []
374        if not (obj or nam or typ):
375            return self.val
376        if obj:
377            return [ntv.to_obj() for ntv in self.val]
378        return [(ntv.name if nam else '') + (' - ' if nam and typ else '') +
379                (ntv.type_str if typ else '') for ntv in self.val]
380
381    def expand(self, full=True, entity=True):
382        '''return a json representation of the triplet (name, type, value)
383
384
385        *Parameters*
386
387        - **full**: Boolean (default True) - If False only members with non empty values are present
388        - **entity**: Boolean (default True) - If True, member with entity name is added
389        '''
390        exp = {ENTITY: self.__class__.__name__} if entity else {}
391        if isinstance(self, NtvList) and full:
392            return exp | {NAME: self.name, TYPE: self.type_str,
393                          VALUE: [ntv.expand(full) for ntv in self.val]}
394        if isinstance(self, NtvSingle) and full:
395            return exp | {NAME: self.name, TYPE: self.type_str, VALUE: self.val}
396        exp |= {} if not self.name else {NAME: self.name}
397        if self.type_str not in ['json', '']:
398            exp[TYPE] = self.type_str
399        if isinstance(self, NtvList):
400            exp[VALUE] = [ntv.expand(full) for ntv in self.val]
401        else:
402            exp[VALUE] = self.val
403        return exp
404
405    def from_value(self):
406        '''return a Ntv entity from ntv_value'''
407        if isinstance(self.ntv_value, list):
408            return NtvList(self.ntv_value)
409        return Ntv.from_obj(self.ntv_value)
410
411    def json_name(self, def_type=None, string=False, explicit=False):
412        '''return the JSON name of the NTV entity (json-ntv format)
413
414        *Parameters*
415
416        - **def_typ** : Datatype or Namespace (default None) - type of the parent entity
417        - **string** : boolean (default False) - If True, return a string else a tuple
418        - **explicit** : boolean (default False) - If True, type is always included'''
419        if def_type is None:
420            def_type = ''
421        elif isinstance(def_type, (Datatype, Namespace)):
422            def_type = def_type.long_name
423        json_name = self.ntv_name if self.ntv_name else ''
424        json_type = relative_type(
425            def_type, self.type_str) if self.ntv_type else ''
426        implicit = isinstance(self, NtvSingle) and (
427            json_type == 'json' and (
428                not def_type or def_type == 'json' or def_type[-1] == '.')
429            or not NtvConnector.is_json_class(self.val))
430        if implicit and not explicit:
431            json_type = ''
432        json_sep = self._obj_sep(json_name, json_type, def_type)
433        if string:
434            return json_name + json_sep + json_type
435        return [json_name, json_sep, json_type]
436
437    @staticmethod
438    def lower(val1, val2):
439        ''' compare two ntv_value and return True if val1 < val2, False if val1 > val2 and
440        None in the other cases'''
441        res = None
442        for v1, v2 in zip(Ntv.obj(val1).tree.leaf_nodes, Ntv.obj(val2).tree.leaf_nodes):
443            if v1.val is None:
444                res = True
445            elif isinstance(v1.val, (dict, list)):
446                res = Ntv.lower(v1.val, v2)
447            elif isinstance(v2.val, (dict, list)):
448                res = Ntv.lower(v1, v2.val)
449            elif isinstance(v1.val, Number):
450                if isinstance(v2.val, Number):
451                    res = None if v1.val == v2.val else v1.val < v2.val
452                else:
453                    res = False
454            elif isinstance(v1.val, str):
455                if isinstance(v2.val, Number):
456                    res = True
457                elif isinstance(v2.val, str):
458                    res = None if v1.val == v2.val else v1.val < v2.val
459                else:
460                    res = False
461            if not res is None:
462                break
463        return res
464
465    def no_type(self):
466        '''convert NTV entity in a NV entity (in which ntv_type is 'json' or None')'''
467        no_typ = copy.copy(self)
468        for ntv in NtvTree(no_typ).leaf_nodes:
469            ntv.set_type('json')
470        for ntv in NtvTree(no_typ).inner_nodes:
471            ntv.set_type()
472        return no_typ
473
474    def no_name(self):
475        '''convert NTV entity in a TV entity (in which ntv_name is None)'''
476        no_nam = copy.copy(self)
477        for ntv in NtvTree(no_nam):
478            ntv.ntv_name = None
479        return no_nam
480
481    def no_value(self):
482        '''convert NTV entity in a NV entity (in which ntv_value of leaf nodes is ntv_type )'''
483        no_val = copy.copy(self)
484        for ntv in NtvTree(no_val).leaf_nodes:
485            ntv.ntv_value = ntv.type_str
486            ntv.set_type('json')
487        return no_val
488
489    def only_type(self):
490        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
491        is ntv_type )'''
492        only_typ = copy.copy(self)
493        for ntv in NtvTree(only_typ).leaf_nodes:
494            ntv.ntv_value = ntv.type_str
495            ntv.set_type('json')
496            ntv.set_name()
497        for ntv in NtvTree(only_typ).inner_nodes:
498            ntv.set_name()
499            ntv.set_type()
500        return only_typ
501
502    def only_name(self):
503        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
504        is ntv_name )'''
505        only_nam = copy.copy(self)
506        for ntv in NtvTree(only_nam).leaf_nodes:
507            ntv.ntv_value = ntv.name
508            ntv.set_type('json')
509            ntv.set_name()
510        for ntv in NtvTree(only_nam).inner_nodes:
511            ntv.set_type()
512        return only_nam
513
514    def only_value(self):
515        '''convert NTV entity in a V entity'''
516        only_val = copy.copy(self)
517        for ntv in NtvTree(only_val).leaf_nodes:
518            ntv.set_type('json')
519            ntv.set_name()
520        for ntv in NtvTree(only_val).inner_nodes:
521            ntv.set_type()
522            ntv.set_name()
523        return only_val
524
525    @staticmethod
526    def obj_ntv(value, name='', typ='', single=False):
527        '''return a json-ntv representation without using Ntv structure.
528
529        *Parameters*
530
531        - **value** : ntv-value of the json-ntv
532        - **name** : string (default '') - ntv-name of the json-ntv
533        - **typ** : string (default '') - ntv_type of the json-ntv
534        - **single** : boolean (default False) - if True, value is a single object
535        else value is a set of objetcs.
536        '''
537        value = {} if not value else value
538        name = '' if not name else name
539        typ = '' if not typ else typ
540        ntv_list = isinstance(value, (list, dict))
541        if not single and not ntv_list:
542            raise NtvError('value is not compatible with not single NTV data')
543        sep = ':' if single else '::'
544        sep = '' if not typ and (
545            not single or single and not ntv_list) else sep
546        name += sep + typ
547        value = [value] if not name and isinstance(
548            value, dict) and len(value) == 1 else value
549        return {name: value} if name else value
550
551    def pointer(self, index=False, item_idx=None):
552        '''return a nested list of pointer from root
553
554        *Parameters*
555
556        - **index**: Boolean (default False) - use index instead of name
557        - **item_idx**: Integer (default None) - index value for the pointer
558        (useful with duplicate data)'''
559        if not self.parent:
560            root_pointer = 0 if index else self.json_name(string=True)
561            return NtvPointer([root_pointer])
562        idx = item_idx if item_idx else self.parent.ntv_value.index(self)
563        num = index or self.parent.json_array
564        pointer = self.parent.pointer(index)
565        pointer.append(idx if num else self.json_name_str)
566        return pointer
567
568    def reduce(self, obj=True, maxi=6, level=3):
569        '''reduce the length and the level of the entity
570
571        *Parameters*
572
573        - **obj**: boolean (default True) - If True return jsonNTV else NTV entity
574        - **maxi**: integer (default 6) - Number of returned entities in an NtvList
575        - **level**: integer (default 6) - returned entities in an NtvList at this level is None
576
577        *return*
578
579        - **NTV entity** or **jsonNTV**
580        '''
581        ntv = copy.copy(self)
582        cont = Ntv.obj('___') if self.json_array else Ntv.obj({'___': ''})
583        if isinstance(self, NtvSingle):
584            return ntv
585        if level == 0:
586            ntv.ntv_value = [Ntv.obj('___', no_typ=True)]
587        if len(self) <= maxi:
588            ntv.ntv_value = [child.reduce(False, maxi, level-1)
589                             for child in ntv]
590            return ntv
591        mid = maxi // 2
592        cont.set_type(ntv.type_str)
593        start = [child.reduce(False, maxi, level-1) for child in ntv[:mid]]
594        middle = [cont]
595        end = [child.reduce(False, maxi, level-1) for child in ntv[-mid:]]
596        ntv.ntv_value = start + middle + end
597        if obj:
598            return ntv.to_obj()
599        return ntv
600
601    def remove(self, first=True, index=None):
602        '''remove self from its parent entity.
603
604        *parameters*
605
606        - **first** : boolean (default True) - if True only the first instance
607        else all
608        - **index** : integer (default None) - index of self in its parent
609        '''
610        parent = self.parent
611        if not parent:
612            return
613        idx = parent.ntv_value.index(self) if index is None else index
614        if not parent[idx] == self:
615            raise NtvError('the entity is not present at the index')
616        del parent.ntv_value[idx]
617        if not first and index is None:
618            while self in parent:
619                idx = parent.ntv_value.index(self)
620                del parent.ntv_value[idx]
621        if not self in parent:
622            self.parent = None
623        return
624
625    def replace(self, ntv):
626        '''replace self by ntv in the tree'''
627        parent = self.parent
628        if parent:
629            idx = parent.ntv_value.index(self)
630            parent.insert(idx, ntv)
631            del parent[idx+1]
632            if not self in parent:
633                self.parent = None
634        else:
635            raise NtvError('replace is not available for root node')
636
637    def set_name(self, name='', nodes='simple'):
638        '''set new names to the entity
639
640        *Parameters*
641
642        - **name**: list or string (default '') - New name values
643        - **nodes**: string (default 'simple') - nodes to be changed
644            'simple': current entity
645            'leaves': NtvSingle entities
646            'inner': NtvList entities
647            'all': all entities  '''
648        name = '' if name is None else name
649        match nodes:
650            case 'simple':
651                self.ntv_name = str(name)
652            case 'leaves':
653                if not isinstance(name, list):
654                    name = [str(name)] * NtvTree(self).breadth
655                for nam, ntv in zip(name, NtvTree(self).leaf_nodes):
656                    ntv.ntv_name = nam
657            case 'inner':
658                if not isinstance(name, list):
659                    name = [str(name)] * len(NtvTree(self).inner_nodes)
660                for nam, ntv in zip(name, NtvTree(self).inner_nodes):
661                    ntv.ntv_name = nam
662            case 'all':
663                if not isinstance(name, list):
664                    name = [str(name)] * NtvTree(self).size
665                for nam, ntv in zip(name, NtvTree(self).nodes):
666                    ntv.ntv_name = nam
667            case _:
668                raise NtvError('the nodes option is not valid')
669
670    def set_type(self, typ=None):
671        '''set a new type to the entity
672
673        *Parameters*
674
675        - **typ**: string, Datatype, Namespace (default None)'''
676        if typ and not isinstance(typ, (str, Datatype, Namespace)):
677            raise NtvError('the type is not a valid type')
678        self.ntv_type = str_type(typ, self.__class__.__name__ == 'NtvSingle')
679
680    def set_value(self, value=None, fast=False):
681        '''set new ntv_value of a single entity or of a list of entities included
682
683        *Parameters*
684
685        - **value**: list or single value
686        - **fast** : boolean (default False) - if True, value is not converted'''
687        if isinstance(self, NtvSingle):
688            self.ntv_value = NtvSingle(value, ntv_type=self.ntv_type,
689                                       fast=fast).val
690            return
691        if not isinstance(value, list):
692            value = [value] * NtvTree(self).breadth
693        ntv_val = NtvList(value, fast=fast)
694        for val, ntv in zip(ntv_val, NtvTree(self).leaf_nodes):
695            ntv.ntv_value = val.val
696        return
697
698    def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
699        '''convert NtvList entity to NtvSingle entity
700
701        *Parameters*
702
703        - **ntv_name** : String (default None) - name of the NTV entity
704        - **ntv_type**: String (default None) - type of the entity
705        - **value**: value of the entity
706        - **fast**: boolean (default False) - Ntv is created with a list of json values
707        without control
708        '''
709        return NtvSingle(self.obj_value(def_type=def_type, **kwargs),
710                         self.name if self.name else name,
711                         self.type_str if self.type_str else typ)
712
713    def to_ntvlist(self, def_type=None, def_sep=None, no_typ=False, decode_str=False,
714                   typ_auto=False, fast=False):
715        '''convert NtvSingle entity to NtvList entity
716
717        *Parameters*
718
719        - **value**: Ntv value to convert in an Ntv entity
720        - **no_typ** : boolean (default None) - if True, NtvList is with 'json' type
721        - **def_type** : Datatype or Namespace (default None) - default type of the value
722        - **def_sep**: ':', '::' or None (default None) - default separator of the value
723        - **decode_str**: boolean (default False) - if True, string are loaded as json data
724        - **type_auto**: boolean (default False) - if True, default type for NtvList
725        is the ntv_type of the first Ntv in the ntv_value
726        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
727        '''
728        ntv = Ntv.from_obj(self.ntv_value, def_type, def_sep, no_typ, decode_str,
729                           typ_auto, fast)
730        if ntv.__class__.__name__ == 'NtvSingle':
731            return NtvList([self])
732        if self.ntv_name:
733            ntv.set_name(self.ntv_name)
734        return ntv
735
736    def to_mermaid(self, title='', disp=False, row=False, leaves=False):
737        '''return a mermaid flowchart.
738
739        *Parameters*
740
741        - **title**: String (default '') - title of the flowchart
742        - **disp**: Boolean (default False) - if true, return a display else return
743        a mermaid text diagram
744        - **row**: Boolean (default False) - if True, add the node row
745        - **leaves**: Boolean (default False) - if True, add the leaf row
746        '''
747        option = {'title': title, 'disp': disp, 'row': row, 'leaves': leaves}
748        if disp:
749            Ntv.obj({':$mermaid': self.to_obj()}).to_obj(
750                format='obj', **option)
751            return None
752        return Ntv.obj({':$mermaid': self.to_obj()}).to_obj(format='obj', **option)
753
754    def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
755        '''return a simple json representation of the Ntv entity.
756
757        *Parameters*
758
759        - **nam**: Boolean(default True) - if true, the names are included
760        - **typ**: Boolean(default True) - if true, the types are included
761        - **val**: Boolean(default True) - if true, the values are included
762        - **jsn**: Boolean(default False) - if false, the 'json' type is not included
763        - **maxi**: Integer (default 10) - number of values to include for NtvList
764        entities. If maxi < 1 all the values are included.
765        '''
766        ntv = self.code_ntv
767        if nam and typ:
768            ntv = ntv[0]
769        if self.ntv_name and nam:
770            ntv += '-' + self.ntv_name
771        if self.ntv_type and typ and (jsn or self.ntv_type.long_name != 'json'):
772            ntv += '-' + self.ntv_type.long_name
773        clas = self.__class__.__name__
774        clas_val = self.ntv_value.__class__.__name__
775        if clas == 'NtvSingle' and clas_val != 'NtvSingle':
776            if val:
777                if ntv:
778                    ntv += '-'
779                ntv += json.dumps(self.ntv_value, cls=NtvJsonEncoder)
780            return ntv
781        if clas == 'NtvSingle' and clas_val == 'NtvSingle':
782            return {ntv:  self.ntv_value.to_repr(nam, typ, val, jsn, maxi)}
783        if clas == 'NtvList':
784            maxv = len(self.ntv_value) if maxi < 1 else maxi
785            return {ntv:  [ntvi.to_repr(nam, typ, val, jsn, maxi)
786                           for ntvi in self.ntv_value[:maxv]]}
787        raise NtvError('the ntv entity is not consistent')
788
789    def to_name(self, default=''):
790        '''return the name of the NTV entity
791
792        *Parameters*
793
794        - **default**: string (default ''): returned value if name is not present '''
795        if self.ntv_name == '':
796            return default
797        return self.ntv_name
798
799    def to_fast(self, def_type=None, **kwargs):
800        '''return the JSON representation of the NTV entity (json-ntv format)
801        without value conversion.
802
803        *Parameters*
804
805        - **def_type** : Datatype or Namespace (default None) - default type to apply
806        to the NTV entity
807        - **encoded** : boolean (default False) - choice for return format
808        (string/bytes if True, dict/list/tuple else)
809        - **format**  : string (default 'json')- choice for return format
810        (json, cbor, obj)
811        - **simpleval** : boolean (default False) - if True, only value (without
812        name and type) is included
813        - **name** : boolean (default true) - if False, name is not included
814        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
815        - **maxi**: Integer (default -1) - number of values to include for NtvList
816        entities. If maxi < 1 all the values are included.
817        '''
818        option = kwargs | {'fast': True}
819        return self.to_obj(def_type=def_type, **option)
820
821    def to_obj(self, def_type=None, **kwargs):
822        '''return the JSON representation of the NTV entity (json-ntv format).
823
824        *Parameters*
825
826        - **def_type** : Datatype or Namespace (default None) - default type to apply
827        to the NTV entity
828        - **encoded** : boolean (default False) - choice for return format
829        (string/bytes if True, dict/list/tuple else)
830        - **format**  : string (default 'json')- choice for return format
831        (json, cbor, obj)
832        - **simpleval** : boolean (default False) - if True, only value (without
833        name and type) is included
834        - **name** : boolean (default true) - if False, name is not included
835        - **type** : boolean (default False) - if True, type is always included
836        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
837        - **fast** : boolean (default False) - if True, json is created without conversion
838        - **maxi**: Integer (default -1) - number of values to include for NtvList
839        entities. If maxi < 1 all the values are included.
840        '''
841        option = {'encoded': False, 'format': 'json', 'fast': False, 'maxi': -1,
842                  'simpleval': False, 'name': True, 'json_array': False,
843                  'type': False} | kwargs
844        value = self.obj_value(def_type=def_type, **option)
845        obj_name = self.json_name(def_type)
846        if not option['name']:
847            obj_name[0] = ''
848        if option['simpleval']:
849            name = ''
850        elif (option['format'] in ('cbor', 'obj') and
851              not NtvConnector.is_json_class(value) and not option['type']):
852            name = obj_name[0]
853        else:
854            name = obj_name[0] + obj_name[1] + obj_name[2]
855        value = [value] if not name and isinstance(
856            value, dict) and len(value) == 1 else value
857        json_obj = {name: value} if name else value
858        if option['encoded'] and option['format'] == 'json':
859            return json.dumps(json_obj, cls=NtvJsonEncoder)
860        if option['encoded'] and option['format'] == 'cbor':
861            return NtvConnector.connector()['CborConnec'].to_obj_ntv(json_obj)
862        return json_obj
863
864    def to_json_ntv(self):
865        ''' create a copy where ntv-value of the self-tree nodes is converted
866        in json-value'''
867        ntv = copy.copy(self)
868        for leaf in ntv.tree.leaf_nodes:
869            if isinstance(leaf.ntv_value, (NtvSingle, NtvList)):
870                leaf.ntv_value = leaf.ntv_value.to_obj()
871                leaf.ntv_type = Datatype('ntv')
872            elif not leaf.is_json:
873                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.cast(
874                    leaf.ntv_value, leaf.ntv_name, leaf.type_str)
875                leaf.ntv_type = Datatype(type_str)
876                leaf.is_json = True
877        return ntv
878
879    def to_obj_ntv(self, **kwargs):
880        ''' create a copy where ntv-value of the self-tree nodes is converted
881        in object-value
882
883        *Parameters*
884
885        - **kwargs** : parameters used in NtvConnector class (specific for each Connector)'''
886        ntv = copy.copy(self)
887        for leaf in ntv.tree.leaf_nodes:
888            if (leaf.is_json and leaf.type_str in set(NtvConnector.dic_type.values())
889                    or leaf.ntv_type is None):
890                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.uncast(
891                    leaf, **kwargs)
892                leaf.ntv_type = Datatype(type_str) if type_str else None
893                leaf.is_json = NtvConnector.is_json(leaf.ntv_value)
894        return ntv
895
896    def to_tuple(self, maxi=10):
897        '''return a nested tuple representation of the NTV entity
898        (NtvList/NtvSingle, ntv_name, ntv_type, ntv_value).
899
900        *Parameters*
901
902        - **maxi**: Integer (default 10) - number of values to include for NtvList
903        entities. If maxi < 1 all the values are included.
904        '''
905        clas = self.__class__.__name__
906        val = self.ntv_value
907        name = self.ntv_name
908        typ = None
909        if self.ntv_type:
910            typ = self.ntv_type.long_name
911        if isinstance(self, NtvSingle) and not isinstance(val, NtvSingle):
912            return (clas, name, typ, val)
913        if isinstance(self, NtvSingle) and isinstance(val, NtvSingle):
914            return (clas, name, typ, val.to_tuple(maxi=maxi))
915        if isinstance(self, NtvList):
916            maxv = len(self.ntv_value) if maxi < 1 else maxi
917            return (clas, name, typ, [ntv.to_tuple(maxi=maxi) for ntv in val[:maxv]])
918        raise NtvError('the ntv entity is not consistent')
919
920    def validate(self, unique=False):
921        '''check the ntv_type validity and return a boolean and a list of errors
922
923        *Parameters*
924
925        - **unique**: boolean (default False) - if True, stop validation at the
926        first error'''
927        errors = []
928        for ntv in self.tree.leaf_nodes:
929            valid = ntv.ntv_type.validate(ntv.ntv_value)
930            if not valid:
931                errors.append(str(ntv.pointer()))
932                if unique:
933                    return (False, errors)
934        return (not errors, errors)
935
936    @abstractmethod
937    def obj_value(self, def_type=None, **kwargs):
938        '''return the ntv_value with different formats defined by kwargs (abstract method)'''
939
940    @property
941    @abstractmethod
942    def json_array(self):
943        ''' return the json_array dynamic attribute (abstract method)'''
944
945    @abstractmethod
946    def _obj_sep(self, json_name, json_type, def_type=None):
947        ''' return separator to include in json_name (abstract method)'''
948
949    @staticmethod
950    def _from_value(value, decode_str=False):
951        '''return a decoded value
952
953        *Parameters*
954
955        - **decode_str**: boolean (default False) - if True, string are loaded as json data
956        '''
957        if isinstance(value, bytes):
958            value = Ntv.from_obj({'$cbor': value}).ntv_value
959        elif decode_str and isinstance(value, str) and value.lstrip() and\
960                value.lstrip()[0] in '"-{[0123456789tfn':
961            try:
962                value = json.loads(value)
963            except json.JSONDecodeError:
964                pass
965        return value
966
967    @staticmethod
968    def decode_json(json_value):
969        '''return (value, name, type, separator, isjson) of a json object'''
970        if isinstance(json_value, dict) and len(json_value) == 1:
971            json_name = list(json_value.keys())[0]
972            val = json_value[json_name]
973            return (val, *NtvUtil.from_obj_name(json_name), NtvConnector.is_json(val))
974        return (json_value, None, None, None, NtvConnector.is_json(json_value))
975
976    @staticmethod
977    def _create_ntvlist(str_typ, def_type, sep, ntv_value, typ_auto, no_typ, ntv_name, fast):
978        '''return a NtvList with parameters from Ntv.from_obj method'''
979        def_type = agreg_type(str_typ, def_type, False)
980        sep_val = ':' if sep and def_type else None
981        if isinstance(ntv_value, dict):
982            keys = list(ntv_value.keys())
983            values = list(ntv_value.values())
984            ntv_list = [Ntv.from_obj({key: val}, def_type, sep_val, fast=fast)
985                        for key, val in zip(keys, values)]
986        else:
987            ntv_list = [Ntv.from_obj(val, def_type, sep_val, fast=fast)
988                        for val in ntv_value]
989        if typ_auto and not def_type and ntv_list:
990            def_type = ntv_list[0].ntv_type
991        def_type = None if no_typ else def_type
992        return NtvList(ntv_list, ntv_name, def_type, typ_auto, fast=fast)
993
994    @staticmethod
995    def _listed(idx):
996        '''transform a tuple of tuple object in a list of list object'''
997        return [val if not isinstance(val, tuple) else Ntv._listed(val) for val in idx]

The Ntv class is an abstract class used by NtvSingleand NtvList classes.

Attributes :

  • ntv_name : String - name of the NTV entity
  • ntv_type: Datatype or Namespace - type of the entity
  • ntv_value: value of the entity

Internal attributes :

  • parent: parent NtvList entity
  • is_json: True if ntv_value is a json_value

dynamic values (@property)

The methods defined in this class are :

Ntv constructor (staticmethod)

NTV conversion (instance methods)

export - conversion (instance methods)

tree methods (instance methods)

other instance methods

utility methods

Ntv(ntv_value, ntv_name, ntv_type)
104    def __init__(self, ntv_value, ntv_name, ntv_type):
105        '''Ntv constructor.
106
107        *Parameters*
108
109        - **ntv_value**: Json entity - value of the entity
110        - **ntv_name** : String (default None) - name of the NTV entity
111        - **ntv_type**: String or Datatype or Namespace (default None) - type of the entity
112        '''
113        if ntv_type.__class__.__name__ in ['Datatype', 'Namespace']:
114            self.ntv_type = ntv_type
115        elif ntv_type and ntv_type[-1] != '.':
116            self.ntv_type = Datatype(ntv_type)
117        elif ntv_type and ntv_type[-1] == '.':
118            self.ntv_type = Namespace.add(ntv_type)
119        else:
120            self.ntv_type = None
121        if not isinstance(ntv_name, str):
122            ntv_name = ''
123        self.ntv_name = ntv_name
124        self.ntv_value = ntv_value
125        self.is_json = NtvConnector.is_json(ntv_value)
126        self.parent = None

Ntv constructor.

Parameters

  • ntv_value: Json entity - value of the entity
  • ntv_name : String (default None) - name of the NTV entity
  • ntv_type: String or Datatype or Namespace (default None) - type of the entity
ntv_name
ntv_value
is_json
parent
@staticmethod
def fast(data, no_typ=False, typ_auto=False):
128    @staticmethod
129    def fast(data, no_typ=False, typ_auto=False):
130        ''' return an Ntv entity from data without conversion.
131
132        *Parameters* : see `obj` method'''
133        return Ntv.obj(data, no_typ=no_typ, typ_auto=typ_auto, fast=True)

return an Ntv entity from data without conversion.

Parameters : see obj method

@staticmethod
def obj(data, no_typ=False, decode_str=False, typ_auto=False, fast=False):
135    @staticmethod
136    def obj(data, no_typ=False, decode_str=False, typ_auto=False, fast=False):
137        ''' return an Ntv entity from data.
138
139        *Parameters*
140
141        - **Data** can be :
142            - a tuple with value, name, typ and cat (see `from_att` method)
143            - a value to decode (see `from_obj`method)
144        - **no_typ** : boolean (default False) - if True, NtvList is with None type
145        - **type_auto**: boolean (default False) - if True, default type for NtvList
146        is the ntv_type of the first Ntv in the ntv_value
147        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
148        - **decode_str**: boolean (default False) - if True, string are loaded in json data'''
149        if isinstance(data, tuple):
150            return Ntv.from_att(*data, decode_str=decode_str, fast=fast)
151        if isinstance(data, str):
152            try:
153                data = json.loads(data)
154            except json.JSONDecodeError:
155                pass
156        return Ntv.from_obj(data, no_typ=no_typ, decode_str=decode_str,
157                            typ_auto=typ_auto, fast=fast)

return an Ntv entity from data.

Parameters

  • Data can be :
    • a tuple with value, name, typ and cat (see from_att method)
    • a value to decode (see from_objmethod)
  • no_typ : boolean (default False) - if True, NtvList is with None type
  • type_auto: boolean (default False) - if True, default type for NtvList is the ntv_type of the first Ntv in the ntv_value
  • fast : boolean (default False) - if True, Ntv entity is created without conversion
  • decode_str: boolean (default False) - if True, string are loaded in json data
@staticmethod
def from_att(value, name, typ, cat, decode_str=False, fast=False):
159    @staticmethod
160    def from_att(value, name, typ, cat, decode_str=False, fast=False):
161        ''' return an Ntv entity.
162
163        *Parameters*
164
165        - **value**: Ntv entity or value to convert in an Ntv entity
166        - **name** : string - name of the Ntv entity
167        - **typ** : string or NtvType - type of the NTV entity
168        - **cat**: string - NTV category ('single', 'list')
169        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
170        - **decode_str**: boolean (default False) - if True, string are loaded as json data'''
171
172        value = Ntv._from_value(value, decode_str)
173        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
174            return value
175        if isinstance(value, list) and cat == 'list':
176            return NtvList(value, name, typ, fast=fast)
177        if cat == 'single':
178            return NtvSingle(value, name, typ, fast=fast)
179        return Ntv.from_obj(value, def_type=typ, fast=fast)

return an Ntv entity.

Parameters

  • value: Ntv entity or value to convert in an Ntv entity
  • name : string - name of the Ntv entity
  • typ : string or NtvType - type of the NTV entity
  • cat: string - NTV category ('single', 'list')
  • fast : boolean (default False) - if True, Ntv entity is created without conversion
  • decode_str: boolean (default False) - if True, string are loaded as json data
@staticmethod
def from_obj( value, def_type=None, def_sep=None, no_typ=False, decode_str=False, typ_auto=False, fast=False):
181    @staticmethod
182    def from_obj(value, def_type=None, def_sep=None, no_typ=False, decode_str=False,
183                 typ_auto=False, fast=False):
184        ''' return an Ntv entity from an object value.
185
186        *Parameters*
187
188        - **value**: Ntv value to convert in an Ntv entity
189        - **no_typ** : boolean (default None) - if True, NtvList is with None type
190        - **def_type** : Datatype or Namespace (default None) - default type of the value
191        - **def_sep**: ':', '::' or None (default None) - default separator of the value
192        - **decode_str**: boolean (default False) - if True, string are loaded as json data
193        - **type_auto**: boolean (default False) - if True, default type for NtvList
194        is the ntv_type of the first Ntv in the ntv_value
195        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion'''
196        value = Ntv._from_value(value, decode_str)
197        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
198            return value
199        ntv_value, ntv_name, str_typ, sep, is_json = Ntv.decode_json(value)
200        sep = def_sep if not sep else sep
201        sep = None if str_typ and str_typ[-1] == '.' and sep == ':' else sep
202        if isinstance(ntv_value, (list, dict)) and sep in (None, '::'):
203            return Ntv._create_ntvlist(str_typ, def_type, sep, ntv_value,
204                                       typ_auto, no_typ, ntv_name, fast)
205        if sep == ':' or (sep is None and isinstance(ntv_value, dict)):
206            ntv_type = agreg_type(str_typ, def_type, False)
207            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
208        if sep is None and not isinstance(ntv_value, dict):
209            is_single_json = isinstance(ntv_value, (int, str, float, bool))
210            ntv_type = agreg_type(str_typ, def_type, is_single_json)
211            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
212        raise NtvError('separator ":" is not compatible with value')

return an Ntv entity from an object value.

Parameters

  • value: Ntv value to convert in an Ntv entity
  • no_typ : boolean (default None) - if True, NtvList is with None type
  • def_type : Datatype or Namespace (default None) - default type of the value
  • def_sep: ':', '::' or None (default None) - default separator of the value
  • decode_str: boolean (default False) - if True, string are loaded as json data
  • type_auto: boolean (default False) - if True, default type for NtvList is the ntv_type of the first Ntv in the ntv_value
  • fast : boolean (default False) - if True, Ntv entity is created without conversion
code_ntv
297    @property
298    def code_ntv(self):
299        '''return a string with the NTV code composed with 1 to 3 letters:
300        - 'l' (NtvList), 's' (NtvSingle / json_value) or 'o' (NtvSingle / obj_value)
301        - 'N' if ntv_name is present else none
302        - 'T' if ntv_type is present else none'''
303        dic = {'NtvList': 'l', 'NtvSingle': 's'}
304        code = dic[self.__class__.__name__]
305        if isinstance(self, NtvSingle) and not self.is_json:
306            code = 'o'
307        if self.ntv_name:
308            code += 'N'
309        if self.ntv_type and self.ntv_type.long_name != 'json':
310            code += 'T'
311        return code

return a string with the NTV code composed with 1 to 3 letters:

  • 'l' (NtvList), 's' (NtvSingle / json_value) or 'o' (NtvSingle / obj_value)
  • 'N' if ntv_name is present else none
  • 'T' if ntv_type is present else none
json_name_str
313    @property
314    def json_name_str(self):
315        '''return the JSON name of the NTV entity'''
316        return self.json_name(def_type=self.parent.type_str, string=True)

return the JSON name of the NTV entity

max_len
318    @property
319    def max_len(self):
320        '''return the highest len of Ntv entity included'''
321        maxi = len(self)
322        if isinstance(self.ntv_value, (list, set)):
323            maxi = max(maxi, max(ntv.max_len for ntv in self.ntv_value))
324        return maxi

return the highest len of Ntv entity included

name
326    @property
327    def name(self):
328        '''return the ntv_name of the entity'''
329        return self.ntv_name

return the ntv_name of the entity

tree
331    @property
332    def tree(self):
333        '''return a tree with included entities (NtvTree object)'''
334        return NtvTree(self)

return a tree with included entities (NtvTree object)

typebase_str
336    @property
337    def typebase_str(self):
338        '''return a string with the value of the TypeBase of the entity'''
339        if not self.ntv_type:
340            return ''
341        return self.ntv_type.typebase.long_name

return a string with the value of the TypeBase of the entity

type_str
343    @property
344    def type_str(self):
345        '''return a string with the value of the NtvType of the entity'''
346        if not self.ntv_type:
347            return ''
348        return self.ntv_type.long_name

return a string with the value of the NtvType of the entity

val
350    @property
351    def val(self):
352        '''return the ntv_value of the entity'''
353        return self.ntv_value

return the ntv_value of the entity

def alike(self, ntv_value):
355    def alike(self, ntv_value):
356        ''' return a Ntv entity with same name and type.
357
358        *Parameters*
359
360        - **ntv_value**: list of ntv values'''
361        return self.__class__(ntv_value, self.ntv_name, self.ntv_type)

return a Ntv entity with same name and type.

Parameters

  • ntv_value: list of ntv values
def childs(self, obj=False, nam=False, typ=False):
363    def childs(self, obj=False, nam=False, typ=False):
364        ''' return a list of child Ntv entities or child data
365
366        *parameters*
367
368        - **obj**: boolean (default False) - return json-value
369        - **nam**: boolean (default False) - return name (with or without type)
370        - **typ**: boolean (default False) - return type (with or without name)
371        '''
372        if isinstance(self, NtvSingle):
373            return []
374        if not (obj or nam or typ):
375            return self.val
376        if obj:
377            return [ntv.to_obj() for ntv in self.val]
378        return [(ntv.name if nam else '') + (' - ' if nam and typ else '') +
379                (ntv.type_str if typ else '') for ntv in self.val]

return a list of child Ntv entities or child data

parameters

  • obj: boolean (default False) - return json-value
  • nam: boolean (default False) - return name (with or without type)
  • typ: boolean (default False) - return type (with or without name)
def expand(self, full=True, entity=True):
381    def expand(self, full=True, entity=True):
382        '''return a json representation of the triplet (name, type, value)
383
384
385        *Parameters*
386
387        - **full**: Boolean (default True) - If False only members with non empty values are present
388        - **entity**: Boolean (default True) - If True, member with entity name is added
389        '''
390        exp = {ENTITY: self.__class__.__name__} if entity else {}
391        if isinstance(self, NtvList) and full:
392            return exp | {NAME: self.name, TYPE: self.type_str,
393                          VALUE: [ntv.expand(full) for ntv in self.val]}
394        if isinstance(self, NtvSingle) and full:
395            return exp | {NAME: self.name, TYPE: self.type_str, VALUE: self.val}
396        exp |= {} if not self.name else {NAME: self.name}
397        if self.type_str not in ['json', '']:
398            exp[TYPE] = self.type_str
399        if isinstance(self, NtvList):
400            exp[VALUE] = [ntv.expand(full) for ntv in self.val]
401        else:
402            exp[VALUE] = self.val
403        return exp

return a json representation of the triplet (name, type, value)

Parameters

  • full: Boolean (default True) - If False only members with non empty values are present
  • entity: Boolean (default True) - If True, member with entity name is added
def from_value(self):
405    def from_value(self):
406        '''return a Ntv entity from ntv_value'''
407        if isinstance(self.ntv_value, list):
408            return NtvList(self.ntv_value)
409        return Ntv.from_obj(self.ntv_value)

return a Ntv entity from ntv_value

def json_name(self, def_type=None, string=False, explicit=False):
411    def json_name(self, def_type=None, string=False, explicit=False):
412        '''return the JSON name of the NTV entity (json-ntv format)
413
414        *Parameters*
415
416        - **def_typ** : Datatype or Namespace (default None) - type of the parent entity
417        - **string** : boolean (default False) - If True, return a string else a tuple
418        - **explicit** : boolean (default False) - If True, type is always included'''
419        if def_type is None:
420            def_type = ''
421        elif isinstance(def_type, (Datatype, Namespace)):
422            def_type = def_type.long_name
423        json_name = self.ntv_name if self.ntv_name else ''
424        json_type = relative_type(
425            def_type, self.type_str) if self.ntv_type else ''
426        implicit = isinstance(self, NtvSingle) and (
427            json_type == 'json' and (
428                not def_type or def_type == 'json' or def_type[-1] == '.')
429            or not NtvConnector.is_json_class(self.val))
430        if implicit and not explicit:
431            json_type = ''
432        json_sep = self._obj_sep(json_name, json_type, def_type)
433        if string:
434            return json_name + json_sep + json_type
435        return [json_name, json_sep, json_type]

return the JSON name of the NTV entity (json-ntv format)

Parameters

  • def_typ : Datatype or Namespace (default None) - type of the parent entity
  • string : boolean (default False) - If True, return a string else a tuple
  • explicit : boolean (default False) - If True, type is always included
@staticmethod
def lower(val1, val2):
437    @staticmethod
438    def lower(val1, val2):
439        ''' compare two ntv_value and return True if val1 < val2, False if val1 > val2 and
440        None in the other cases'''
441        res = None
442        for v1, v2 in zip(Ntv.obj(val1).tree.leaf_nodes, Ntv.obj(val2).tree.leaf_nodes):
443            if v1.val is None:
444                res = True
445            elif isinstance(v1.val, (dict, list)):
446                res = Ntv.lower(v1.val, v2)
447            elif isinstance(v2.val, (dict, list)):
448                res = Ntv.lower(v1, v2.val)
449            elif isinstance(v1.val, Number):
450                if isinstance(v2.val, Number):
451                    res = None if v1.val == v2.val else v1.val < v2.val
452                else:
453                    res = False
454            elif isinstance(v1.val, str):
455                if isinstance(v2.val, Number):
456                    res = True
457                elif isinstance(v2.val, str):
458                    res = None if v1.val == v2.val else v1.val < v2.val
459                else:
460                    res = False
461            if not res is None:
462                break
463        return res

compare two ntv_value and return True if val1 < val2, False if val1 > val2 and None in the other cases

def no_type(self):
465    def no_type(self):
466        '''convert NTV entity in a NV entity (in which ntv_type is 'json' or None')'''
467        no_typ = copy.copy(self)
468        for ntv in NtvTree(no_typ).leaf_nodes:
469            ntv.set_type('json')
470        for ntv in NtvTree(no_typ).inner_nodes:
471            ntv.set_type()
472        return no_typ

convert NTV entity in a NV entity (in which ntv_type is 'json' or None')

def no_name(self):
474    def no_name(self):
475        '''convert NTV entity in a TV entity (in which ntv_name is None)'''
476        no_nam = copy.copy(self)
477        for ntv in NtvTree(no_nam):
478            ntv.ntv_name = None
479        return no_nam

convert NTV entity in a TV entity (in which ntv_name is None)

def no_value(self):
481    def no_value(self):
482        '''convert NTV entity in a NV entity (in which ntv_value of leaf nodes is ntv_type )'''
483        no_val = copy.copy(self)
484        for ntv in NtvTree(no_val).leaf_nodes:
485            ntv.ntv_value = ntv.type_str
486            ntv.set_type('json')
487        return no_val

convert NTV entity in a NV entity (in which ntv_value of leaf nodes is ntv_type )

def only_type(self):
489    def only_type(self):
490        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
491        is ntv_type )'''
492        only_typ = copy.copy(self)
493        for ntv in NtvTree(only_typ).leaf_nodes:
494            ntv.ntv_value = ntv.type_str
495            ntv.set_type('json')
496            ntv.set_name()
497        for ntv in NtvTree(only_typ).inner_nodes:
498            ntv.set_name()
499            ntv.set_type()
500        return only_typ

convert NTV entity in a V entity (in which ntv_value of leaf nodes is ntv_type )

def only_name(self):
502    def only_name(self):
503        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
504        is ntv_name )'''
505        only_nam = copy.copy(self)
506        for ntv in NtvTree(only_nam).leaf_nodes:
507            ntv.ntv_value = ntv.name
508            ntv.set_type('json')
509            ntv.set_name()
510        for ntv in NtvTree(only_nam).inner_nodes:
511            ntv.set_type()
512        return only_nam

convert NTV entity in a V entity (in which ntv_value of leaf nodes is ntv_name )

def only_value(self):
514    def only_value(self):
515        '''convert NTV entity in a V entity'''
516        only_val = copy.copy(self)
517        for ntv in NtvTree(only_val).leaf_nodes:
518            ntv.set_type('json')
519            ntv.set_name()
520        for ntv in NtvTree(only_val).inner_nodes:
521            ntv.set_type()
522            ntv.set_name()
523        return only_val

convert NTV entity in a V entity

@staticmethod
def obj_ntv(value, name='', typ='', single=False):
525    @staticmethod
526    def obj_ntv(value, name='', typ='', single=False):
527        '''return a json-ntv representation without using Ntv structure.
528
529        *Parameters*
530
531        - **value** : ntv-value of the json-ntv
532        - **name** : string (default '') - ntv-name of the json-ntv
533        - **typ** : string (default '') - ntv_type of the json-ntv
534        - **single** : boolean (default False) - if True, value is a single object
535        else value is a set of objetcs.
536        '''
537        value = {} if not value else value
538        name = '' if not name else name
539        typ = '' if not typ else typ
540        ntv_list = isinstance(value, (list, dict))
541        if not single and not ntv_list:
542            raise NtvError('value is not compatible with not single NTV data')
543        sep = ':' if single else '::'
544        sep = '' if not typ and (
545            not single or single and not ntv_list) else sep
546        name += sep + typ
547        value = [value] if not name and isinstance(
548            value, dict) and len(value) == 1 else value
549        return {name: value} if name else value

return a json-ntv representation without using Ntv structure.

Parameters

  • value : ntv-value of the json-ntv
  • name : string (default '') - ntv-name of the json-ntv
  • typ : string (default '') - ntv_type of the json-ntv
  • single : boolean (default False) - if True, value is a single object else value is a set of objetcs.
def pointer(self, index=False, item_idx=None):
551    def pointer(self, index=False, item_idx=None):
552        '''return a nested list of pointer from root
553
554        *Parameters*
555
556        - **index**: Boolean (default False) - use index instead of name
557        - **item_idx**: Integer (default None) - index value for the pointer
558        (useful with duplicate data)'''
559        if not self.parent:
560            root_pointer = 0 if index else self.json_name(string=True)
561            return NtvPointer([root_pointer])
562        idx = item_idx if item_idx else self.parent.ntv_value.index(self)
563        num = index or self.parent.json_array
564        pointer = self.parent.pointer(index)
565        pointer.append(idx if num else self.json_name_str)
566        return pointer

return a nested list of pointer from root

Parameters

  • index: Boolean (default False) - use index instead of name
  • item_idx: Integer (default None) - index value for the pointer (useful with duplicate data)
def reduce(self, obj=True, maxi=6, level=3):
568    def reduce(self, obj=True, maxi=6, level=3):
569        '''reduce the length and the level of the entity
570
571        *Parameters*
572
573        - **obj**: boolean (default True) - If True return jsonNTV else NTV entity
574        - **maxi**: integer (default 6) - Number of returned entities in an NtvList
575        - **level**: integer (default 6) - returned entities in an NtvList at this level is None
576
577        *return*
578
579        - **NTV entity** or **jsonNTV**
580        '''
581        ntv = copy.copy(self)
582        cont = Ntv.obj('___') if self.json_array else Ntv.obj({'___': ''})
583        if isinstance(self, NtvSingle):
584            return ntv
585        if level == 0:
586            ntv.ntv_value = [Ntv.obj('___', no_typ=True)]
587        if len(self) <= maxi:
588            ntv.ntv_value = [child.reduce(False, maxi, level-1)
589                             for child in ntv]
590            return ntv
591        mid = maxi // 2
592        cont.set_type(ntv.type_str)
593        start = [child.reduce(False, maxi, level-1) for child in ntv[:mid]]
594        middle = [cont]
595        end = [child.reduce(False, maxi, level-1) for child in ntv[-mid:]]
596        ntv.ntv_value = start + middle + end
597        if obj:
598            return ntv.to_obj()
599        return ntv

reduce the length and the level of the entity

Parameters

  • obj: boolean (default True) - If True return jsonNTV else NTV entity
  • maxi: integer (default 6) - Number of returned entities in an NtvList
  • level: integer (default 6) - returned entities in an NtvList at this level is None

return

  • NTV entity or jsonNTV
def remove(self, first=True, index=None):
601    def remove(self, first=True, index=None):
602        '''remove self from its parent entity.
603
604        *parameters*
605
606        - **first** : boolean (default True) - if True only the first instance
607        else all
608        - **index** : integer (default None) - index of self in its parent
609        '''
610        parent = self.parent
611        if not parent:
612            return
613        idx = parent.ntv_value.index(self) if index is None else index
614        if not parent[idx] == self:
615            raise NtvError('the entity is not present at the index')
616        del parent.ntv_value[idx]
617        if not first and index is None:
618            while self in parent:
619                idx = parent.ntv_value.index(self)
620                del parent.ntv_value[idx]
621        if not self in parent:
622            self.parent = None
623        return

remove self from its parent entity.

parameters

  • first : boolean (default True) - if True only the first instance else all
  • index : integer (default None) - index of self in its parent
def replace(self, ntv):
625    def replace(self, ntv):
626        '''replace self by ntv in the tree'''
627        parent = self.parent
628        if parent:
629            idx = parent.ntv_value.index(self)
630            parent.insert(idx, ntv)
631            del parent[idx+1]
632            if not self in parent:
633                self.parent = None
634        else:
635            raise NtvError('replace is not available for root node')

replace self by ntv in the tree

def set_name(self, name='', nodes='simple'):
637    def set_name(self, name='', nodes='simple'):
638        '''set new names to the entity
639
640        *Parameters*
641
642        - **name**: list or string (default '') - New name values
643        - **nodes**: string (default 'simple') - nodes to be changed
644            'simple': current entity
645            'leaves': NtvSingle entities
646            'inner': NtvList entities
647            'all': all entities  '''
648        name = '' if name is None else name
649        match nodes:
650            case 'simple':
651                self.ntv_name = str(name)
652            case 'leaves':
653                if not isinstance(name, list):
654                    name = [str(name)] * NtvTree(self).breadth
655                for nam, ntv in zip(name, NtvTree(self).leaf_nodes):
656                    ntv.ntv_name = nam
657            case 'inner':
658                if not isinstance(name, list):
659                    name = [str(name)] * len(NtvTree(self).inner_nodes)
660                for nam, ntv in zip(name, NtvTree(self).inner_nodes):
661                    ntv.ntv_name = nam
662            case 'all':
663                if not isinstance(name, list):
664                    name = [str(name)] * NtvTree(self).size
665                for nam, ntv in zip(name, NtvTree(self).nodes):
666                    ntv.ntv_name = nam
667            case _:
668                raise NtvError('the nodes option is not valid')

set new names to the entity

Parameters

  • name: list or string (default '') - New name values
  • nodes: string (default 'simple') - nodes to be changed 'simple': current entity 'leaves': NtvSingle entities 'inner': NtvList entities 'all': all entities
def set_type(self, typ=None):
670    def set_type(self, typ=None):
671        '''set a new type to the entity
672
673        *Parameters*
674
675        - **typ**: string, Datatype, Namespace (default None)'''
676        if typ and not isinstance(typ, (str, Datatype, Namespace)):
677            raise NtvError('the type is not a valid type')
678        self.ntv_type = str_type(typ, self.__class__.__name__ == 'NtvSingle')

set a new type to the entity

Parameters

  • typ: string, Datatype, Namespace (default None)
def set_value(self, value=None, fast=False):
680    def set_value(self, value=None, fast=False):
681        '''set new ntv_value of a single entity or of a list of entities included
682
683        *Parameters*
684
685        - **value**: list or single value
686        - **fast** : boolean (default False) - if True, value is not converted'''
687        if isinstance(self, NtvSingle):
688            self.ntv_value = NtvSingle(value, ntv_type=self.ntv_type,
689                                       fast=fast).val
690            return
691        if not isinstance(value, list):
692            value = [value] * NtvTree(self).breadth
693        ntv_val = NtvList(value, fast=fast)
694        for val, ntv in zip(ntv_val, NtvTree(self).leaf_nodes):
695            ntv.ntv_value = val.val
696        return

set new ntv_value of a single entity or of a list of entities included

Parameters

  • value: list or single value
  • fast : boolean (default False) - if True, value is not converted
def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
698    def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
699        '''convert NtvList entity to NtvSingle entity
700
701        *Parameters*
702
703        - **ntv_name** : String (default None) - name of the NTV entity
704        - **ntv_type**: String (default None) - type of the entity
705        - **value**: value of the entity
706        - **fast**: boolean (default False) - Ntv is created with a list of json values
707        without control
708        '''
709        return NtvSingle(self.obj_value(def_type=def_type, **kwargs),
710                         self.name if self.name else name,
711                         self.type_str if self.type_str else typ)

convert NtvList entity to NtvSingle entity

Parameters

  • ntv_name : String (default None) - name of the NTV entity
  • ntv_type: String (default None) - type of the entity
  • value: value of the entity
  • fast: boolean (default False) - Ntv is created with a list of json values without control
def to_ntvlist( self, def_type=None, def_sep=None, no_typ=False, decode_str=False, typ_auto=False, fast=False):
713    def to_ntvlist(self, def_type=None, def_sep=None, no_typ=False, decode_str=False,
714                   typ_auto=False, fast=False):
715        '''convert NtvSingle entity to NtvList entity
716
717        *Parameters*
718
719        - **value**: Ntv value to convert in an Ntv entity
720        - **no_typ** : boolean (default None) - if True, NtvList is with 'json' type
721        - **def_type** : Datatype or Namespace (default None) - default type of the value
722        - **def_sep**: ':', '::' or None (default None) - default separator of the value
723        - **decode_str**: boolean (default False) - if True, string are loaded as json data
724        - **type_auto**: boolean (default False) - if True, default type for NtvList
725        is the ntv_type of the first Ntv in the ntv_value
726        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
727        '''
728        ntv = Ntv.from_obj(self.ntv_value, def_type, def_sep, no_typ, decode_str,
729                           typ_auto, fast)
730        if ntv.__class__.__name__ == 'NtvSingle':
731            return NtvList([self])
732        if self.ntv_name:
733            ntv.set_name(self.ntv_name)
734        return ntv

convert NtvSingle entity to NtvList entity

Parameters

  • value: Ntv value to convert in an Ntv entity
  • no_typ : boolean (default None) - if True, NtvList is with 'json' type
  • def_type : Datatype or Namespace (default None) - default type of the value
  • def_sep: ':', '::' or None (default None) - default separator of the value
  • decode_str: boolean (default False) - if True, string are loaded as json data
  • type_auto: boolean (default False) - if True, default type for NtvList is the ntv_type of the first Ntv in the ntv_value
  • fast : boolean (default False) - if True, Ntv entity is created without conversion
def to_mermaid(self, title='', disp=False, row=False, leaves=False):
736    def to_mermaid(self, title='', disp=False, row=False, leaves=False):
737        '''return a mermaid flowchart.
738
739        *Parameters*
740
741        - **title**: String (default '') - title of the flowchart
742        - **disp**: Boolean (default False) - if true, return a display else return
743        a mermaid text diagram
744        - **row**: Boolean (default False) - if True, add the node row
745        - **leaves**: Boolean (default False) - if True, add the leaf row
746        '''
747        option = {'title': title, 'disp': disp, 'row': row, 'leaves': leaves}
748        if disp:
749            Ntv.obj({':$mermaid': self.to_obj()}).to_obj(
750                format='obj', **option)
751            return None
752        return Ntv.obj({':$mermaid': self.to_obj()}).to_obj(format='obj', **option)

return a mermaid flowchart.

Parameters

  • title: String (default '') - title of the flowchart
  • disp: Boolean (default False) - if true, return a display else return a mermaid text diagram
  • row: Boolean (default False) - if True, add the node row
  • leaves: Boolean (default False) - if True, add the leaf row
def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
754    def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
755        '''return a simple json representation of the Ntv entity.
756
757        *Parameters*
758
759        - **nam**: Boolean(default True) - if true, the names are included
760        - **typ**: Boolean(default True) - if true, the types are included
761        - **val**: Boolean(default True) - if true, the values are included
762        - **jsn**: Boolean(default False) - if false, the 'json' type is not included
763        - **maxi**: Integer (default 10) - number of values to include for NtvList
764        entities. If maxi < 1 all the values are included.
765        '''
766        ntv = self.code_ntv
767        if nam and typ:
768            ntv = ntv[0]
769        if self.ntv_name and nam:
770            ntv += '-' + self.ntv_name
771        if self.ntv_type and typ and (jsn or self.ntv_type.long_name != 'json'):
772            ntv += '-' + self.ntv_type.long_name
773        clas = self.__class__.__name__
774        clas_val = self.ntv_value.__class__.__name__
775        if clas == 'NtvSingle' and clas_val != 'NtvSingle':
776            if val:
777                if ntv:
778                    ntv += '-'
779                ntv += json.dumps(self.ntv_value, cls=NtvJsonEncoder)
780            return ntv
781        if clas == 'NtvSingle' and clas_val == 'NtvSingle':
782            return {ntv:  self.ntv_value.to_repr(nam, typ, val, jsn, maxi)}
783        if clas == 'NtvList':
784            maxv = len(self.ntv_value) if maxi < 1 else maxi
785            return {ntv:  [ntvi.to_repr(nam, typ, val, jsn, maxi)
786                           for ntvi in self.ntv_value[:maxv]]}
787        raise NtvError('the ntv entity is not consistent')

return a simple json representation of the Ntv entity.

Parameters

  • nam: Boolean(default True) - if true, the names are included
  • typ: Boolean(default True) - if true, the types are included
  • val: Boolean(default True) - if true, the values are included
  • jsn: Boolean(default False) - if false, the 'json' type is not included
  • maxi: Integer (default 10) - number of values to include for NtvList entities. If maxi < 1 all the values are included.
def to_name(self, default=''):
789    def to_name(self, default=''):
790        '''return the name of the NTV entity
791
792        *Parameters*
793
794        - **default**: string (default ''): returned value if name is not present '''
795        if self.ntv_name == '':
796            return default
797        return self.ntv_name

return the name of the NTV entity

Parameters

  • default: string (default ''): returned value if name is not present
def to_fast(self, def_type=None, **kwargs):
799    def to_fast(self, def_type=None, **kwargs):
800        '''return the JSON representation of the NTV entity (json-ntv format)
801        without value conversion.
802
803        *Parameters*
804
805        - **def_type** : Datatype or Namespace (default None) - default type to apply
806        to the NTV entity
807        - **encoded** : boolean (default False) - choice for return format
808        (string/bytes if True, dict/list/tuple else)
809        - **format**  : string (default 'json')- choice for return format
810        (json, cbor, obj)
811        - **simpleval** : boolean (default False) - if True, only value (without
812        name and type) is included
813        - **name** : boolean (default true) - if False, name is not included
814        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
815        - **maxi**: Integer (default -1) - number of values to include for NtvList
816        entities. If maxi < 1 all the values are included.
817        '''
818        option = kwargs | {'fast': True}
819        return self.to_obj(def_type=def_type, **option)

return the JSON representation of the NTV entity (json-ntv format) without value conversion.

Parameters

  • def_type : Datatype or Namespace (default None) - default type to apply to the NTV entity
  • encoded : boolean (default False) - choice for return format (string/bytes if True, dict/list/tuple else)
  • format : string (default 'json')- choice for return format (json, cbor, obj)
  • simpleval : boolean (default False) - if True, only value (without name and type) is included
  • name : boolean (default true) - if False, name is not included
  • json_array : boolean (default false) - if True, Json-object is not used for NtvList
  • maxi: Integer (default -1) - number of values to include for NtvList entities. If maxi < 1 all the values are included.
def to_obj(self, def_type=None, **kwargs):
821    def to_obj(self, def_type=None, **kwargs):
822        '''return the JSON representation of the NTV entity (json-ntv format).
823
824        *Parameters*
825
826        - **def_type** : Datatype or Namespace (default None) - default type to apply
827        to the NTV entity
828        - **encoded** : boolean (default False) - choice for return format
829        (string/bytes if True, dict/list/tuple else)
830        - **format**  : string (default 'json')- choice for return format
831        (json, cbor, obj)
832        - **simpleval** : boolean (default False) - if True, only value (without
833        name and type) is included
834        - **name** : boolean (default true) - if False, name is not included
835        - **type** : boolean (default False) - if True, type is always included
836        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
837        - **fast** : boolean (default False) - if True, json is created without conversion
838        - **maxi**: Integer (default -1) - number of values to include for NtvList
839        entities. If maxi < 1 all the values are included.
840        '''
841        option = {'encoded': False, 'format': 'json', 'fast': False, 'maxi': -1,
842                  'simpleval': False, 'name': True, 'json_array': False,
843                  'type': False} | kwargs
844        value = self.obj_value(def_type=def_type, **option)
845        obj_name = self.json_name(def_type)
846        if not option['name']:
847            obj_name[0] = ''
848        if option['simpleval']:
849            name = ''
850        elif (option['format'] in ('cbor', 'obj') and
851              not NtvConnector.is_json_class(value) and not option['type']):
852            name = obj_name[0]
853        else:
854            name = obj_name[0] + obj_name[1] + obj_name[2]
855        value = [value] if not name and isinstance(
856            value, dict) and len(value) == 1 else value
857        json_obj = {name: value} if name else value
858        if option['encoded'] and option['format'] == 'json':
859            return json.dumps(json_obj, cls=NtvJsonEncoder)
860        if option['encoded'] and option['format'] == 'cbor':
861            return NtvConnector.connector()['CborConnec'].to_obj_ntv(json_obj)
862        return json_obj

return the JSON representation of the NTV entity (json-ntv format).

Parameters

  • def_type : Datatype or Namespace (default None) - default type to apply to the NTV entity
  • encoded : boolean (default False) - choice for return format (string/bytes if True, dict/list/tuple else)
  • format : string (default 'json')- choice for return format (json, cbor, obj)
  • simpleval : boolean (default False) - if True, only value (without name and type) is included
  • name : boolean (default true) - if False, name is not included
  • type : boolean (default False) - if True, type is always included
  • json_array : boolean (default false) - if True, Json-object is not used for NtvList
  • fast : boolean (default False) - if True, json is created without conversion
  • maxi: Integer (default -1) - number of values to include for NtvList entities. If maxi < 1 all the values are included.
def to_json_ntv(self):
864    def to_json_ntv(self):
865        ''' create a copy where ntv-value of the self-tree nodes is converted
866        in json-value'''
867        ntv = copy.copy(self)
868        for leaf in ntv.tree.leaf_nodes:
869            if isinstance(leaf.ntv_value, (NtvSingle, NtvList)):
870                leaf.ntv_value = leaf.ntv_value.to_obj()
871                leaf.ntv_type = Datatype('ntv')
872            elif not leaf.is_json:
873                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.cast(
874                    leaf.ntv_value, leaf.ntv_name, leaf.type_str)
875                leaf.ntv_type = Datatype(type_str)
876                leaf.is_json = True
877        return ntv

create a copy where ntv-value of the self-tree nodes is converted in json-value

def to_obj_ntv(self, **kwargs):
879    def to_obj_ntv(self, **kwargs):
880        ''' create a copy where ntv-value of the self-tree nodes is converted
881        in object-value
882
883        *Parameters*
884
885        - **kwargs** : parameters used in NtvConnector class (specific for each Connector)'''
886        ntv = copy.copy(self)
887        for leaf in ntv.tree.leaf_nodes:
888            if (leaf.is_json and leaf.type_str in set(NtvConnector.dic_type.values())
889                    or leaf.ntv_type is None):
890                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.uncast(
891                    leaf, **kwargs)
892                leaf.ntv_type = Datatype(type_str) if type_str else None
893                leaf.is_json = NtvConnector.is_json(leaf.ntv_value)
894        return ntv

create a copy where ntv-value of the self-tree nodes is converted in object-value

Parameters

  • kwargs : parameters used in NtvConnector class (specific for each Connector)
def to_tuple(self, maxi=10):
896    def to_tuple(self, maxi=10):
897        '''return a nested tuple representation of the NTV entity
898        (NtvList/NtvSingle, ntv_name, ntv_type, ntv_value).
899
900        *Parameters*
901
902        - **maxi**: Integer (default 10) - number of values to include for NtvList
903        entities. If maxi < 1 all the values are included.
904        '''
905        clas = self.__class__.__name__
906        val = self.ntv_value
907        name = self.ntv_name
908        typ = None
909        if self.ntv_type:
910            typ = self.ntv_type.long_name
911        if isinstance(self, NtvSingle) and not isinstance(val, NtvSingle):
912            return (clas, name, typ, val)
913        if isinstance(self, NtvSingle) and isinstance(val, NtvSingle):
914            return (clas, name, typ, val.to_tuple(maxi=maxi))
915        if isinstance(self, NtvList):
916            maxv = len(self.ntv_value) if maxi < 1 else maxi
917            return (clas, name, typ, [ntv.to_tuple(maxi=maxi) for ntv in val[:maxv]])
918        raise NtvError('the ntv entity is not consistent')

return a nested tuple representation of the NTV entity (NtvList/NtvSingle, ntv_name, ntv_type, ntv_value).

Parameters

  • maxi: Integer (default 10) - number of values to include for NtvList entities. If maxi < 1 all the values are included.
def validate(self, unique=False):
920    def validate(self, unique=False):
921        '''check the ntv_type validity and return a boolean and a list of errors
922
923        *Parameters*
924
925        - **unique**: boolean (default False) - if True, stop validation at the
926        first error'''
927        errors = []
928        for ntv in self.tree.leaf_nodes:
929            valid = ntv.ntv_type.validate(ntv.ntv_value)
930            if not valid:
931                errors.append(str(ntv.pointer()))
932                if unique:
933                    return (False, errors)
934        return (not errors, errors)

check the ntv_type validity and return a boolean and a list of errors

Parameters

  • unique: boolean (default False) - if True, stop validation at the first error
@abstractmethod
def obj_value(self, def_type=None, **kwargs):
936    @abstractmethod
937    def obj_value(self, def_type=None, **kwargs):
938        '''return the ntv_value with different formats defined by kwargs (abstract method)'''

return the ntv_value with different formats defined by kwargs (abstract method)

json_array
940    @property
941    @abstractmethod
942    def json_array(self):
943        ''' return the json_array dynamic attribute (abstract method)'''

return the json_array dynamic attribute (abstract method)

@staticmethod
def decode_json(json_value):
967    @staticmethod
968    def decode_json(json_value):
969        '''return (value, name, type, separator, isjson) of a json object'''
970        if isinstance(json_value, dict) and len(json_value) == 1:
971            json_name = list(json_value.keys())[0]
972            val = json_value[json_name]
973            return (val, *NtvUtil.from_obj_name(json_name), NtvConnector.is_json(val))
974        return (json_value, None, None, None, NtvConnector.is_json(json_value))

return (value, name, type, separator, isjson) of a json object

Inherited Members
json_ntv.ntv_util.NtvUtil
is_dictable
from_obj_name
decode_ntv_tab
to_ntvpointer
class NtvSingle(Ntv):
1000class NtvSingle(Ntv):
1001    ''' A NTV-single entity is a Ntv entity not composed with other entities.
1002
1003    *Attributes :*
1004    - no additional attributes to those of parent class `Ntv`
1005
1006    *dynamic values (@property)*
1007    - `json_array`
1008
1009    The additional methods defined in this class are :
1010
1011    *instance methods*
1012    - `obj_value`
1013    '''
1014
1015    def __init__(self, value, ntv_name=None, ntv_type=None, fast=False):
1016        '''NtvSingle constructor.
1017
1018        *Parameters*
1019
1020        - **ntv_name** : String (default None) - name of the NTV entity
1021        - **ntv_type**: String (default None) - type of the entity
1022        - **value**: value of the entity
1023        - **fast**: boolean (default False) - Ntv is created with a list of json values
1024        without control
1025        '''
1026        if not fast:
1027            value, ntv_name, ntv_type = NtvSingle._decode_s(
1028                value, ntv_name, ntv_type)
1029            if ntv_type and isinstance(ntv_type, str) and ntv_type[-1] == '.':
1030                raise NtvError('the ntv_type is not valid')
1031        super().__init__(value, ntv_name, ntv_type)
1032
1033    def __eq__(self, other):
1034        ''' equal if name type and value are equal'''
1035        return self.__class__.__name__ == other.__class__.__name__ and\
1036            self.ntv_name == other.ntv_name and self.ntv_type == other.ntv_type and\
1037            self.ntv_value == other.ntv_value
1038
1039    def __hash__(self):
1040        '''return hash(name) + hash(type) + hash(value)'''
1041        return hash(self.ntv_name) + hash(self.ntv_type) + \
1042            hash(json.dumps(self.ntv_value, cls=NtvJsonEncoder))
1043
1044    def __copy__(self):
1045        ''' Copy all the Ntv tree '''
1046        return self.__class__(copy.copy(self.ntv_value), self.ntv_name,
1047                              self.ntv_type, fast=True)
1048
1049    @property
1050    def json_array(self):
1051        ''' return the json_array dynamic attribute (always False)'''
1052        return False
1053
1054    def obj_value(self, def_type=None, **kwargs):
1055        '''return the ntv_value with different formats defined by kwargs'''
1056        option = {'encoded': False, 'format': 'json',
1057                  'simpleval': False, 'fast': False} | kwargs
1058        if option['fast'] or option['format'] in ('json', 'tuple'):
1059            return self.ntv_value
1060        if option['format'] == 'obj' and self.ntv_value == 'null':
1061            return None
1062        return NtvConnector.uncast(self, **option)[0]
1063
1064    def _obj_sep(self, json_name, json_type, def_type=None):  # REQ5
1065        ''' return separator to include in json_name'''
1066        if json_type or not def_type and isinstance(self.ntv_value, (list, dict)):
1067            return ':'
1068        return ''
1069
1070    @staticmethod
1071    def _decode_s(ntv_value, ntv_name, ntv_type_str):
1072        '''return adjusted ntv_value, ntv_name, ntv_type(str)'''
1073        is_json = NtvConnector.is_json(ntv_value)
1074        name = None
1075        if is_json:
1076            if isinstance(ntv_value, (list)):
1077                ntv_value = [NtvSingle._decode_s(val, '', ntv_type_str)[
1078                    0] for val in ntv_value]
1079            elif isinstance(ntv_value, (dict)):
1080                ntv_value = {key: NtvSingle._decode_s(val, '', ntv_type_str)[
1081                    0] for key, val in ntv_value.items()}
1082        elif isinstance(ntv_value, NtvSingle):
1083            ntv_value = ntv_value.to_obj()
1084            return (ntv_value, ntv_name, 'ntv')
1085        else:
1086            ntv_value, name, typ_str = NtvConnector.cast(ntv_value, ntv_name)
1087            ntv_type_str = Datatype(typ_str).name if typ_str else ntv_type_str
1088        if not ntv_type_str:
1089            if is_json:
1090                ntv_type_str = 'json'
1091            else:
1092                ntv_type_str = typ_str
1093        elif not is_json and ntv_type_str != typ_str:
1094            raise NtvError('ntv_value is not compatible with ntv_type')
1095        ntv_name = name if not ntv_name else ntv_name
1096        return (ntv_value, ntv_name, ntv_type_str)

A NTV-single entity is a Ntv entity not composed with other entities.

Attributes :

  • no additional attributes to those of parent class Ntv

dynamic values (@property)

The additional methods defined in this class are :

instance methods

NtvSingle(value, ntv_name=None, ntv_type=None, fast=False)
1015    def __init__(self, value, ntv_name=None, ntv_type=None, fast=False):
1016        '''NtvSingle constructor.
1017
1018        *Parameters*
1019
1020        - **ntv_name** : String (default None) - name of the NTV entity
1021        - **ntv_type**: String (default None) - type of the entity
1022        - **value**: value of the entity
1023        - **fast**: boolean (default False) - Ntv is created with a list of json values
1024        without control
1025        '''
1026        if not fast:
1027            value, ntv_name, ntv_type = NtvSingle._decode_s(
1028                value, ntv_name, ntv_type)
1029            if ntv_type and isinstance(ntv_type, str) and ntv_type[-1] == '.':
1030                raise NtvError('the ntv_type is not valid')
1031        super().__init__(value, ntv_name, ntv_type)

NtvSingle constructor.

Parameters

  • ntv_name : String (default None) - name of the NTV entity
  • ntv_type: String (default None) - type of the entity
  • value: value of the entity
  • fast: boolean (default False) - Ntv is created with a list of json values without control
json_array
1049    @property
1050    def json_array(self):
1051        ''' return the json_array dynamic attribute (always False)'''
1052        return False

return the json_array dynamic attribute (always False)

def obj_value(self, def_type=None, **kwargs):
1054    def obj_value(self, def_type=None, **kwargs):
1055        '''return the ntv_value with different formats defined by kwargs'''
1056        option = {'encoded': False, 'format': 'json',
1057                  'simpleval': False, 'fast': False} | kwargs
1058        if option['fast'] or option['format'] in ('json', 'tuple'):
1059            return self.ntv_value
1060        if option['format'] == 'obj' and self.ntv_value == 'null':
1061            return None
1062        return NtvConnector.uncast(self, **option)[0]

return the ntv_value with different formats defined by kwargs

class NtvList(Ntv):
1099class NtvList(Ntv):
1100    '''A NTV-list entity is a Ntv entity where:
1101
1102    - ntv_value is a list of NTV entities,
1103    - ntv_type is a default type available for included NTV entities
1104
1105    *Attributes :*
1106    - no additional attributes to those of parent class `Ntv`
1107
1108    *dynamic values (@property)*
1109    - `json_array`
1110
1111    The additional methods defined in this class are :
1112
1113    *instance methods*
1114    - `obj_value`
1115    '''
1116
1117    def __init__(self, list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False):
1118        '''NtvList constructor.
1119
1120        *Parameters*
1121
1122        - **ntv_name** : String (default None) - name of the NTV entity
1123        - **ntv_type**: String (default None) - default type or namespace of
1124        the included entities
1125        - **list_ntv**: list - list of Ntv objects or obj_value of Ntv objects
1126        - **fast**: boolean (default False) - if True, Ntv is created with a list
1127        of json values without control
1128        - **type_auto**: boolean (default False) - if True, default type for NtvList
1129        is the ntv_type of the first Ntv in the ntv_value'''
1130        if isinstance(list_ntv, NtvList):
1131            ntv_value = [copy.copy(ntv) for ntv in list_ntv.ntv_value]
1132            ntv_type = list_ntv.ntv_type
1133            ntv_name = list_ntv.ntv_name
1134        elif isinstance(list_ntv, list):
1135            ntv_value = [Ntv.from_obj(ntv, ntv_type, ':', fast=fast)
1136                         for ntv in list_ntv]
1137        elif isinstance(list_ntv, dict):
1138            ntv_value = [Ntv.from_obj({key: val}, ntv_type, ':', fast=fast)
1139                         for key, val in list_ntv.items()]
1140        else:
1141            raise NtvError('ntv_value is not a list')
1142        if typ_auto and not ntv_type and len(ntv_value) > 0 and ntv_value[0].ntv_type:
1143            ntv_type = ntv_value[0].ntv_type
1144        super().__init__(ntv_value, ntv_name, ntv_type)
1145        for ntv in self:
1146            ntv.parent = self
1147
1148    @property
1149    def json_array(self):
1150        ''' return the json_array dynamic attribute'''
1151        set_name = {ntv.json_name(
1152            def_type=self.type_str, string=True) for ntv in self}
1153        return '' in set_name or len(set_name) != len(self)
1154
1155    def __eq__(self, other):
1156        ''' equal if name and value are equal'''
1157        return self.__class__.__name__ == other.__class__.__name__ and\
1158            self.ntv_name == other.ntv_name and self.ntv_value == other.ntv_value
1159
1160    def __hash__(self):
1161        '''return hash(name) + hash(value)'''
1162        return hash(self.ntv_name) + hash(tuple(self.ntv_value))
1163
1164    def __copy__(self):
1165        ''' Copy all the data '''
1166        cop = self.__class__(self)
1167        cop.parent = None
1168        return cop
1169
1170    def __setitem__(self, ind, value):
1171        ''' replace ntv_value item at the `ind` row with `value`'''
1172        if ind < 0 or ind >= len(self):
1173            raise NtvError("out of bounds")
1174        self.ntv_value[ind] = value
1175        if isinstance(value, (NtvSingle, NtvList)):
1176            value.parent = self
1177
1178    def __delitem__(self, ind):
1179        '''remove ntv_value item at the `ind` row'''
1180        if isinstance(ind, int):
1181            self.ntv_value.pop(ind)
1182        else:
1183            self.ntv_value.pop(self.ntv_value.index(self[ind]))
1184
1185    def append(self, ntv):
1186        ''' add ntv at the end of the list of Ntv entities included'''
1187        old_parent = ntv.parent
1188        if old_parent:
1189            del old_parent[old_parent.ntv_value.index(ntv)]
1190        self.ntv_value.append(ntv)
1191        ntv.parent = self
1192
1193    def insert(self, idx, ntv):
1194        ''' add ntv at the index idx of the list of Ntv entities included'''
1195        old_parent = ntv.parent
1196        if old_parent:
1197            del old_parent[old_parent.ntv_value.index(ntv)]
1198        self.ntv_value.insert(idx, ntv)
1199        ntv.parent = self
1200
1201    def _obj_sep(self, json_name, json_type, def_type=None):
1202        ''' return separator to include in json_name'''
1203        sep = ':' if (json_type and json_type[-1] == '.') else '::'
1204        return sep if (json_type and json_type[-1] != '.') or (json_type and json_name) else ''
1205
1206    def obj_value(self, def_type=None, **kwargs):
1207        '''return the ntv_value with different formats defined by kwargs
1208        '''
1209        option = {'encoded': False, 'format': 'json', 'simpleval': False,
1210                  'json_array': False, 'fast': False, 'maxi': -1} | kwargs
1211        opt2 = option | {'encoded': False}
1212        maxv = len(self.ntv_value) if option['maxi'] < 1 else option['maxi']
1213        def_type = self.ntv_type.long_name if self.ntv_type else def_type
1214        values = [ntv.to_obj(def_type=def_type, **opt2)
1215                  for ntv in self.ntv_value[:maxv]]
1216        if len(self) == 1 and isinstance(self[0], NtvSingle) and isinstance(values[0], dict):
1217            return values[0]
1218        if not NtvUtil.is_dictable(values) or option['simpleval'] or option['json_array']:
1219            return values
1220        return {list(val.items())[0][0]: list(val.items())[0][1] for val in values}

A NTV-list entity is a Ntv entity where:

  • ntv_value is a list of NTV entities,
  • ntv_type is a default type available for included NTV entities

Attributes :

  • no additional attributes to those of parent class Ntv

dynamic values (@property)

The additional methods defined in this class are :

instance methods

NtvList(list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False)
1117    def __init__(self, list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False):
1118        '''NtvList constructor.
1119
1120        *Parameters*
1121
1122        - **ntv_name** : String (default None) - name of the NTV entity
1123        - **ntv_type**: String (default None) - default type or namespace of
1124        the included entities
1125        - **list_ntv**: list - list of Ntv objects or obj_value of Ntv objects
1126        - **fast**: boolean (default False) - if True, Ntv is created with a list
1127        of json values without control
1128        - **type_auto**: boolean (default False) - if True, default type for NtvList
1129        is the ntv_type of the first Ntv in the ntv_value'''
1130        if isinstance(list_ntv, NtvList):
1131            ntv_value = [copy.copy(ntv) for ntv in list_ntv.ntv_value]
1132            ntv_type = list_ntv.ntv_type
1133            ntv_name = list_ntv.ntv_name
1134        elif isinstance(list_ntv, list):
1135            ntv_value = [Ntv.from_obj(ntv, ntv_type, ':', fast=fast)
1136                         for ntv in list_ntv]
1137        elif isinstance(list_ntv, dict):
1138            ntv_value = [Ntv.from_obj({key: val}, ntv_type, ':', fast=fast)
1139                         for key, val in list_ntv.items()]
1140        else:
1141            raise NtvError('ntv_value is not a list')
1142        if typ_auto and not ntv_type and len(ntv_value) > 0 and ntv_value[0].ntv_type:
1143            ntv_type = ntv_value[0].ntv_type
1144        super().__init__(ntv_value, ntv_name, ntv_type)
1145        for ntv in self:
1146            ntv.parent = self

NtvList constructor.

Parameters

  • ntv_name : String (default None) - name of the NTV entity
  • ntv_type: String (default None) - default type or namespace of the included entities
  • list_ntv: list - list of Ntv objects or obj_value of Ntv objects
  • fast: boolean (default False) - if True, Ntv is created with a list of json values without control
  • type_auto: boolean (default False) - if True, default type for NtvList is the ntv_type of the first Ntv in the ntv_value
json_array
1148    @property
1149    def json_array(self):
1150        ''' return the json_array dynamic attribute'''
1151        set_name = {ntv.json_name(
1152            def_type=self.type_str, string=True) for ntv in self}
1153        return '' in set_name or len(set_name) != len(self)

return the json_array dynamic attribute

def append(self, ntv):
1185    def append(self, ntv):
1186        ''' add ntv at the end of the list of Ntv entities included'''
1187        old_parent = ntv.parent
1188        if old_parent:
1189            del old_parent[old_parent.ntv_value.index(ntv)]
1190        self.ntv_value.append(ntv)
1191        ntv.parent = self

add ntv at the end of the list of Ntv entities included

def insert(self, idx, ntv):
1193    def insert(self, idx, ntv):
1194        ''' add ntv at the index idx of the list of Ntv entities included'''
1195        old_parent = ntv.parent
1196        if old_parent:
1197            del old_parent[old_parent.ntv_value.index(ntv)]
1198        self.ntv_value.insert(idx, ntv)
1199        ntv.parent = self

add ntv at the index idx of the list of Ntv entities included

def obj_value(self, def_type=None, **kwargs):
1206    def obj_value(self, def_type=None, **kwargs):
1207        '''return the ntv_value with different formats defined by kwargs
1208        '''
1209        option = {'encoded': False, 'format': 'json', 'simpleval': False,
1210                  'json_array': False, 'fast': False, 'maxi': -1} | kwargs
1211        opt2 = option | {'encoded': False}
1212        maxv = len(self.ntv_value) if option['maxi'] < 1 else option['maxi']
1213        def_type = self.ntv_type.long_name if self.ntv_type else def_type
1214        values = [ntv.to_obj(def_type=def_type, **opt2)
1215                  for ntv in self.ntv_value[:maxv]]
1216        if len(self) == 1 and isinstance(self[0], NtvSingle) and isinstance(values[0], dict):
1217            return values[0]
1218        if not NtvUtil.is_dictable(values) or option['simpleval'] or option['json_array']:
1219            return values
1220        return {list(val.items())[0][0]: list(val.items())[0][1] for val in values}

return the ntv_value with different formats defined by kwargs