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

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
@staticmethod
def lower(val1, val2):
276    @staticmethod
277    def lower(val1, val2):
278        ''' compare two ntv_value and return True if val1 < val2, False if val1 > val2 and 
279        None in the other cases'''
280        res = None
281        for v1, v2 in zip(Ntv.obj(val1).tree.leaf_nodes, Ntv.obj(val2).tree.leaf_nodes):
282            if v1.val is None:
283                res = True
284            elif isinstance(v1.val, (dict, list)):
285                res = Ntv.lower(v1.val, v2)
286            elif isinstance(v2.val, (dict, list)):
287                res = Ntv.lower(v1, v2.val)
288            elif isinstance(v1.val, Number):
289                if isinstance(v2.val, Number):
290                    res = None if v1.val == v2.val else v1.val < v2.val
291                else:
292                    res = False
293            elif isinstance(v1.val, str):
294                if isinstance(v2.val, Number):
295                    res = True
296                elif isinstance(v2.val, str):
297                    res = None if v1.val == v2.val else v1.val < v2.val
298                else:
299                    res = False
300            if not res is None:
301                break
302        return res 

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

def childs(self, obj=False, nam=False, typ=False):
304    def childs(self, obj=False, nam=False, typ=False):
305        ''' return a list of child Ntv entities or child data
306        
307        *parameters*
308        
309        - **obj**: boolean (default False) - return json-value
310        - **nam**: boolean (default False) - return name (with or without type) 
311        - **typ**: boolean (default False) - return type (with or without name) 
312        '''
313        if isinstance(self, NtvSingle):
314            return []
315        if not (obj or nam or typ):
316            return self.val
317        if obj:
318            return [ntv.to_obj() for ntv in self.val]
319        return [(ntv.name if nam else '') + (' - ' if nam and typ else '') + 
320                (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 pointer(self, index=False, item_idx=None):
322    def pointer(self, index=False, item_idx=None):
323        '''return a nested list of pointer from root
324        
325        *Parameters*
326
327        - **index**: Boolean (default False) - use index instead of name
328        - **item_idx**: Integer (default None) - index value for the pointer 
329        (useful with duplicate data)'''
330        if not self.parent:
331            return NtvPointer([])        
332        idx = item_idx if item_idx else self.parent.ntv_value.index(self)
333        num = index or (self.ntv_name == "" and self.parent.json_array)
334        pointer = self.parent.pointer(index)
335        pointer.append(idx if num else self.ntv_name)
336        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)
code_ntv

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
max_len

return the highest len of Ntv entity included

name

return the ntv_name of the entity

type_str

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

val

return the ntv_value of the entity

def alike(self, ntv_value):
379    def alike(self, ntv_value):
380        ''' return a Ntv entity with same name and type.
381
382        *Parameters*
383
384        - **ntv_value**: list of ntv values'''
385        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 from_value(self):
387    def from_value(self):
388        '''return a Ntv entity from ntv_value'''
389        if isinstance(self.ntv_value, list):
390            return NtvList(self.ntv_value)
391        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):
393    def json_name(self, def_type=None, string=False, explicit=False):
394        '''return the JSON name of the NTV entity (json-ntv format)
395
396        *Parameters*
397
398        - **def_typ** : Datatype or Namespace (default None) - type of the parent entity
399        - **string** : boolean (default False) - If True, return a string else a tuple
400        - **explicit** : boolean (default False) - If True, type is always included'''
401        if def_type is None:
402            def_type = ''
403        elif isinstance(def_type, (Datatype, Namespace)):
404            def_type = def_type.long_name
405        json_name = self.ntv_name if self.ntv_name else ''
406        json_type = relative_type(
407            def_type, self.type_str) if self.ntv_type else ''
408        implicit = isinstance(self, NtvSingle) and (json_type == 'json'  
409                    and (not def_type or def_type == 'json' or def_type[-1] == '.')
410                    or not NtvConnector.is_json_class(self.val))
411        if implicit and not explicit:
412            json_type = ''
413        json_sep = self._obj_sep(json_name, json_type, def_type)
414        if string:
415            return json_name + json_sep + json_type
416        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
def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
418    def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
419        '''convert NtvList entity to NtvSingle entity
420
421        *Parameters*
422
423        - **ntv_name** : String (default None) - name of the NTV entity
424        - **ntv_type**: String (default None) - type of the entity
425        - **value**: value of the entity
426        - **fast**: boolean (default False) - Ntv is created with a list of json values
427        without control
428        '''
429        return NtvSingle(self.obj_value(def_type=def_type, **kwargs),
430                         self.name if self.name else name,
431                         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):
433    def to_ntvlist(self, def_type=None, def_sep=None, no_typ=False, decode_str=False,
434                 typ_auto=False, fast=False):
435        '''convert NtvSingle entity to NtvList entity
436
437        *Parameters*
438
439        - **value**: Ntv value to convert in an Ntv entity
440        - **no_typ** : boolean (default None) - if True, NtvList is with 'json' type
441        - **def_type** : Datatype or Namespace (default None) - default type of the value
442        - **def_sep**: ':', '::' or None (default None) - default separator of the value
443        - **decode_str**: boolean (default False) - if True, string are loaded as json data
444        - **type_auto**: boolean (default False) - if True, default type for NtvList
445        is the ntv_type of the first Ntv in the ntv_value
446        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
447        '''
448        ntv = Ntv.from_obj(self.ntv_value, def_type, def_sep, no_typ, decode_str,
449                     typ_auto, fast)
450        if ntv.__class__.__name__ == 'NtvSingle':
451            return NtvList([self])
452        if self.ntv_name:
453            ntv.set_name(self.ntv_name)
454        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 notype(self):
456    def notype(self):
457        '''convert NTV entity in a NV entity (with ntv_type is 'json' or None')'''
458        no_type = copy.copy(self)
459        for ntv in NtvTree(no_type).leaf_nodes:
460            ntv.set_type('json')           
461        for ntv in NtvTree(no_type).inner_nodes:
462            ntv.set_type()
463        return no_type

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

def reduce(self, obj=True, maxi=6, level=3):
465    def reduce(self, obj=True, maxi=6, level=3):
466        '''reduce the length and the level of the entity
467        
468        *Parameters*
469
470        - **obj**: boolean (default True) - If True return jsonNTV else NTV entity
471        - **maxi**: integer (default 6) - Number of returned entities in an NtvList
472        - **level**: integer (default 6) - returned entities in an NtvList at this level is None
473        
474        *return*
475        
476        - **NTV entity** or **jsonNTV**
477        '''
478        ntv = copy.copy(self)
479        cont = Ntv.obj('___') if self.json_array else Ntv.obj({'___':''})            
480        if isinstance(self, NtvSingle):
481            return ntv
482        if level == 0:
483            #ntv.ntv_value = [NtvSingle('___',ntv_type=ntv.type_str)]
484            ntv.ntv_value = [Ntv.obj('___',no_typ=True)]
485            #ntv.ntv_value = [Ntv.obj('___',typ_auto=True)]
486        if len(self) <= maxi:
487            ntv.ntv_value = [child.reduce(False, maxi, level-1) for child in ntv]
488            return ntv
489        mid = maxi // 2
490        cont.set_type(ntv.type_str)
491        start = [child.reduce(False, maxi, level-1) for child in ntv[:mid]]
492        #middle = [NtvSingle('...',ntv_type=ntv.type_str)]
493        middle = [cont]
494        end = [child.reduce(False, maxi, level-1) for child in ntv[-mid:]]
495        ntv.ntv_value = start + middle + end
496        if obj:
497            return ntv.to_obj()
498        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 set_name(self, name='', nodes='simple'):
500    def set_name(self, name='', nodes='simple'):
501        '''set new names to the entity
502
503        *Parameters*
504
505        - **name**: list or string (default '') - New name values
506        - **nodes**: string (default 'simple') - nodes to be changed
507            'simple': current entity
508            'leaves': NtvSingle entities
509            'inner': NtvList entities
510            'all': all entities  '''
511        name = '' if name is None else name
512        match nodes:
513            case 'simple':
514                self.ntv_name = str(name)
515            case 'leaves':
516                if not isinstance(name, list):
517                    name = [str(name)] * NtvTree(self).breadth
518                for nam, ntv in zip(name, NtvTree(self).leaf_nodes):
519                    ntv.ntv_name = nam
520            case 'inner':
521                if not isinstance(name, list):
522                    name = [str(name)] * len(NtvTree(self).inner_nodes)
523                for nam, ntv in zip(name, NtvTree(self).inner_nodes):
524                    ntv.ntv_name = nam
525            case 'all':
526                if not isinstance(name, list):
527                    name = [str(name)] * NtvTree(self).size
528                for nam, ntv in zip(name, NtvTree(self).nodes):
529                    ntv.ntv_name = nam
530            case _:
531                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):
533    def set_type(self, typ=None):
534        '''set a new type to the entity
535
536        *Parameters*
537
538        - **typ**: string, Datatype, Namespace (default None)'''
539        if typ and not isinstance(typ, (str, Datatype, Namespace)):
540            raise NtvError('the type is not a valid type')
541        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):
543    def set_value(self, value=None, fast=False):
544        '''set new ntv_value of a single entity or of a list of entities included
545
546        *Parameters*
547
548        - **value**: list or single value
549        - **fast** : boolean (default False) - if True, value is not converted'''
550        if isinstance(self, NtvSingle):
551            self.ntv_value = NtvSingle(value, ntv_type=self.ntv_type, 
552                                       fast=fast).val
553            return
554        if not isinstance(value, list):
555            value = [value] * NtvTree(self).breadth
556        ntv_val = NtvList(value, fast=fast)
557        for val, ntv in zip(ntv_val, NtvTree(self).leaf_nodes):
558            ntv.ntv_value = val.val
559        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_mermaid(self, title='', disp=False, row=False, leaves=False):
561    def to_mermaid(self, title='', disp=False, row=False, leaves=False):
562        '''return a mermaid flowchart.
563
564        *Parameters*
565
566        - **title**: String (default '') - title of the flowchart
567        - **disp**: Boolean (default False) - if true, return a display else return
568        a mermaid text diagram
569        - **row**: Boolean (default False) - if True, add the node row
570        - **leaves**: Boolean (default False) - if True, add the leaf row
571        '''
572        option = {'title': title, 'disp': disp, 'row': row, 'leaves': leaves}
573        if disp:
574            Ntv.obj({':$mermaid': self.to_obj()}).to_obj(
575                format='obj', **option)
576            return None
577        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 expand(self, full=True, entity=True):
579    def expand(self, full=True, entity=True):
580        '''return a json representation of the triplet (name, type, value)
581        
582        
583        *Parameters*
584
585        - **full**: Boolean (default True) - If False only members with non empty values are present
586        - **entity**: Boolean (default True) - If True, member with entity name is added
587        '''
588        exp = {ENTITY: self.__class__.__name__} if entity else {} 
589        if isinstance(self, NtvList) and full:
590            return exp | {NAME: self.name, TYPE: self.type_str, 
591                    VALUE: [ntv.expand(full) for ntv in self.val]}
592        if isinstance(self, NtvSingle) and full:
593            return exp | {NAME: self.name, TYPE: self.type_str, VALUE: self.val}
594        exp |= {} if not self.name else {NAME: self.name}
595        if not self.type_str in ['json', ''] :
596            exp[TYPE] = self.type_str
597        if isinstance(self, NtvList):
598            exp[VALUE] = [ntv.expand(full) for ntv in self.val]
599        else:
600            exp[VALUE] = self.val
601        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 to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
603    def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
604        '''return a simple json representation of the Ntv entity.
605
606        *Parameters*
607
608        - **nam**: Boolean(default True) - if true, the names are included
609        - **typ**: Boolean(default True) - if true, the types are included
610        - **val**: Boolean(default True) - if true, the values are included
611        - **jsn**: Boolean(default False) - if false, the 'json' type is not included
612        - **maxi**: Integer (default 10) - number of values to include for NtvList
613        entities. If maxi < 1 all the values are included.
614        '''
615        ntv = self.code_ntv
616        if nam and typ:
617            ntv = ntv[0]
618        if self.ntv_name and nam:
619            ntv += '-' + self.ntv_name
620        if self.ntv_type and typ and (jsn or self.ntv_type.long_name != 'json'):
621            ntv += '-' + self.ntv_type.long_name
622        clas = self.__class__.__name__
623        clas_val = self.ntv_value.__class__.__name__
624        if clas == 'NtvSingle' and clas_val != 'NtvSingle':
625            if val:
626                if ntv:
627                    ntv += '-'
628                ntv += json.dumps(self.ntv_value, cls=NtvJsonEncoder)
629            return ntv
630        if clas == 'NtvSingle' and clas_val == 'NtvSingle':
631            return {ntv:  self.ntv_value.to_repr(nam, typ, val, jsn, maxi)}
632        if clas == 'NtvList':
633            maxv = len(self.ntv_value) if maxi < 1 else maxi
634            return {ntv:  [ntvi.to_repr(nam, typ, val, jsn, maxi) 
635                           for ntvi in self.ntv_value[:maxv]]}
636        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=''):
638    def to_name(self, default=''):
639        '''return the name of the NTV entity
640
641        *Parameters*
642
643        - **default**: string (default ''): returned value if name is not present '''
644        if self.ntv_name == '':
645            return default
646        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):
648    def to_fast(self, def_type=None, **kwargs):
649        '''return the JSON representation of the NTV entity (json-ntv format)
650        without value conversion.
651
652        *Parameters*
653
654        - **def_type** : Datatype or Namespace (default None) - default type to apply
655        to the NTV entity
656        - **encoded** : boolean (default False) - choice for return format
657        (string/bytes if True, dict/list/tuple else)
658        - **format**  : string (default 'json')- choice for return format
659        (json, cbor, obj)
660        - **simpleval** : boolean (default False) - if True, only value (without
661        name and type) is included
662        - **name** : boolean (default true) - if False, name is not included
663        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
664        - **maxi**: Integer (default -1) - number of values to include for NtvList
665        entities. If maxi < 1 all the values are included.
666        '''
667        option = kwargs | {'fast': True}
668        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):
670    def to_obj(self, def_type=None, **kwargs):
671        '''return the JSON representation of the NTV entity (json-ntv format).
672
673        *Parameters*
674
675        - **def_type** : Datatype or Namespace (default None) - default type to apply
676        to the NTV entity
677        - **encoded** : boolean (default False) - choice for return format
678        (string/bytes if True, dict/list/tuple else)
679        - **format**  : string (default 'json')- choice for return format
680        (json, cbor, obj)
681        - **simpleval** : boolean (default False) - if True, only value (without
682        name and type) is included
683        - **name** : boolean (default true) - if False, name is not included
684        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
685        - **fast** : boolean (default False) - if True, json is created without conversion
686        - **maxi**: Integer (default -1) - number of values to include for NtvList
687        entities. If maxi < 1 all the values are included.
688        '''
689        option = {'encoded': False, 'format': 'json', 'fast': False, 'maxi': -1,
690                  'simpleval': False, 'name': True, 'json_array': False} | kwargs
691        value = self.obj_value(def_type=def_type, **option)
692        obj_name = self.json_name(def_type)
693        if not option['name']:
694            obj_name[0] = ''
695        if option['simpleval']:
696            name = ''
697        elif option['format'] in ('cbor', 'obj') and not NtvConnector.is_json_class(value):
698            name = obj_name[0]
699        else:
700            name = obj_name[0] + obj_name[1] + obj_name[2]
701        value = [value] if not name and isinstance(value, dict) and len(value) == 1 else value
702        json_obj = {name: value} if name else value
703        if option['encoded'] and option['format'] == 'json':
704            return json.dumps(json_obj, cls=NtvJsonEncoder)
705        if option['encoded'] and option['format'] == 'cbor':
706            return NtvConnector.connector()['CborConnec'].to_obj_ntv(json_obj)
707        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
  • 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.
@staticmethod
def obj_ntv(value, name='', typ='', single=False):
709    @staticmethod
710    def obj_ntv(value, name='', typ='', single=False):
711        '''return a json-ntv representation without using Ntv structure.
712
713        *Parameters*
714
715        - **value** : ntv-value of the json-ntv
716        - **name** : string (default '') - ntv-name of the json-ntv
717        - **typ** : string (default '') - ntv_type of the json-ntv
718        - **single** : boolean (default False) - if True, value is a single object
719        else value is a set of objetcs.
720        '''
721        value = {} if not value else value
722        name = '' if not name else name
723        typ = '' if not typ else typ
724        ntv_list = isinstance(value, (list, dict))
725        if not single and not ntv_list:
726            raise NtvError('value is not compatible with not single NTV data')
727        sep = ':' if single else '::'
728        sep = '' if not typ and (not single or single and not ntv_list) else sep
729        name += sep + typ
730        value = [value] if not name and isinstance(value, dict) and  len(value) == 1 else value
731        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 to_json_ntv(self):
733    def to_json_ntv(self):
734        ''' create a copy where ntv-value of the self-tree nodes is converted 
735        in json-value'''
736        ntv = copy.copy(self)
737        for leaf in ntv.tree.leaf_nodes:
738            if isinstance(leaf.ntv_value, (NtvSingle, NtvList)):
739                leaf.ntv_value = leaf.ntv_value.to_obj()
740                leaf.ntv_type = Datatype('ntv')
741            elif not leaf.is_json:
742                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.cast(
743                    leaf.ntv_value, leaf.ntv_name, leaf.type_str)
744                leaf.ntv_type = Datatype.add(type_str)
745                leaf.is_json = True
746        return ntv

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

def to_obj_ntv(self, **kwargs):
748    def to_obj_ntv(self, **kwargs):
749        ''' create a copy where ntv-value of the self-tree nodes is converted 
750        in object-value
751
752        *Parameters*
753
754        - **kwargs** : parameters used in NtvConnector class (specific for each Connector)'''
755        ntv = copy.copy(self)
756        for leaf in ntv.tree.leaf_nodes:
757            if (leaf.is_json and leaf.type_str in set(NtvConnector.dic_type.values())
758                    or leaf.ntv_type is None):
759                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.uncast(
760                    leaf, **kwargs)
761                leaf.ntv_type = Datatype.add(type_str) if type_str else None
762                leaf.is_json = NtvConnector.is_json(leaf.ntv_value)
763        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):
765    def to_tuple(self, maxi=10):
766        '''return a nested tuple representation of the NTV entity
767        (NtvList/NtvSingle, ntv_name, ntv_type, ntv_value).
768
769        *Parameters*
770
771        - **maxi**: Integer (default 10) - number of values to include for NtvList
772        entities. If maxi < 1 all the values are included.
773        '''
774        clas = self.__class__.__name__
775        val = self.ntv_value
776        name = self.ntv_name
777        typ = None
778        if self.ntv_type:
779            typ = self.ntv_type.long_name
780        if isinstance(self, NtvSingle) and not isinstance(val, NtvSingle):
781            return (clas, name, typ, val)
782        if isinstance(self, NtvSingle) and isinstance(val, NtvSingle):
783            return (clas, name, typ, val.to_tuple(maxi=maxi))
784        if isinstance(self, NtvList):
785            maxv = len(self.ntv_value) if maxi < 1 else maxi
786            return (clas, name, typ, [ntv.to_tuple(maxi=maxi) for ntv in val[:maxv]])
787        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 remove(self, first=True, index=None):
789    def remove(self, first=True, index=None):
790        '''remove self from its parent entity.
791        
792        *parameters*
793        
794        - **first** : boolean (default True) - if True only the first instance
795        else all
796        - **index** : integer (default None) - index of self in its parent
797        '''
798        parent = self.parent
799        if not parent:
800            return
801        idx = parent.ntv_value.index(self) if index is None else index
802        if not parent[idx] == self:
803            raise NtvError('the entity is not present at the index')
804        del parent.ntv_value[idx]
805        if not first and index is None:
806            while self in parent:
807                idx = parent.ntv_value.index(self)
808                del parent.ntv_value[idx]                
809        if not self in parent:
810            self.parent = None
811        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):
813    def replace(self, ntv):
814        '''replace self by ntv in the tree'''
815        parent = self.parent
816        if parent:
817            idx = parent.ntv_value.index(self)
818            parent.insert(idx, ntv)
819            del parent[idx+1]
820            if not self in parent:
821                self.parent=None
822        else:
823            self = ntv

replace self by ntv in the tree

tree

return a tree with included entities (NtvTree object)

@abstractmethod
def obj_value(self):
830    @abstractmethod
831    def obj_value(self):
832        '''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

return the json_array dynamic attribute (abstract method)

@staticmethod
def decode_json(json_value):
861    @staticmethod
862    def decode_json(json_value):
863        '''return (value, name, type, separator, isjson) of a json object'''
864        if isinstance(json_value, dict) and len(json_value) == 1:
865            json_name = list(json_value.keys())[0]
866            val = json_value[json_name]
867            return (val, *NtvUtil.from_obj_name(json_name), NtvConnector.is_json(val))
868        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
from_obj_name
decode_ntv_tab
class NtvSingle(Ntv):
894class NtvSingle(Ntv):
895    ''' A NTV-single entity is a Ntv entity not composed with other entities.
896
897    *Attributes :*
898    - no additional attributes to those of parent class `Ntv`
899
900    *dynamic values (@property)*
901    - `json_array`
902
903    The additional methods defined in this class are :
904
905    *instance methods*
906    - `obj_value`
907    '''
908
909    def __init__(self, value, ntv_name=None, ntv_type=None, fast=False):
910        '''NtvSingle constructor.
911
912        *Parameters*
913
914        - **ntv_name** : String (default None) - name of the NTV entity
915        - **ntv_type**: String (default None) - type of the entity
916        - **value**: value of the entity
917        - **fast**: boolean (default False) - Ntv is created with a list of json values
918        without control
919        '''
920        if not fast:
921            value, ntv_name, ntv_type = NtvSingle._decode_s(
922                value, ntv_name, ntv_type)
923            if ntv_type and isinstance(ntv_type, str) and ntv_type[-1] == '.':
924                raise NtvError('the ntv_type is not valid')
925        super().__init__(value, ntv_name, ntv_type)
926
927    def __eq__(self, other):
928        ''' equal if name type and value are equal'''
929        return self.__class__.__name__ == other.__class__.__name__ and\
930            self.ntv_name == other.ntv_name and self.ntv_type == other.ntv_type and\
931            self.ntv_value == other.ntv_value
932
933    def __hash__(self):
934        '''return hash(name) + hash(type) + hash(value)'''
935        return hash(self.ntv_name) + hash(self.ntv_type) + \
936            hash(json.dumps(self.ntv_value, cls=NtvJsonEncoder))
937
938    def __copy__(self):
939        ''' Copy all the Ntv tree '''
940        return self.__class__(copy.copy(self.ntv_value), self.ntv_name,
941                              self.ntv_type, fast=True)
942
943    @property
944    def json_array(self):
945        ''' return the json_array dynamic attribute (always False)'''
946        return False
947
948    def obj_value(self, def_type=None, **kwargs):
949        '''return the ntv_value with different formats defined by kwargs'''
950        option = {'encoded': False, 'format': 'json',
951                  'simpleval': False, 'fast': False} | kwargs
952        if option['fast'] or option['format'] in ('json', 'tuple'):
953            return self.ntv_value
954        if option['format'] == 'obj' and self.ntv_value == 'null':
955            return None
956        return NtvConnector.uncast(self, **option)[0]
957
958    def _obj_sep(self, json_name, json_type, def_type=None): # REQ5
959        ''' return separator to include in json_name'''
960        if json_type or not def_type and isinstance(self.ntv_value, (list, dict)):
961            return ':'
962        return ''
963
964    @staticmethod
965    def _decode_s(ntv_value, ntv_name, ntv_type):
966        '''return adjusted ntv_value, ntv_name, ntv_type'''
967        is_json = NtvConnector.is_json(ntv_value)
968        if is_json:
969            if isinstance(ntv_value, (list)):
970                ntv_value = [NtvSingle._decode_s(val, '', ntv_type)[
971                    0] for val in ntv_value]
972            elif isinstance(ntv_value, (dict)):
973                ntv_value = {key: NtvSingle._decode_s(val, '', ntv_type)[
974                    0] for key, val in ntv_value.items()}
975        elif isinstance(ntv_value, NtvSingle):
976            ntv_value = ntv_value.to_obj()
977            return (ntv_value, ntv_name, 'ntv')
978        else:
979            ntv_value, name, typ = NtvConnector.cast(ntv_value, ntv_name)  
980            ntv_type = Datatype(typ) if typ else ntv_type
981        if not ntv_type:
982            if is_json:
983                ntv_type = 'json'
984            else:
985                ntv_type = typ
986                if not ntv_name:
987                    ntv_name = name
988        elif not is_json and Datatype(ntv_type) != Datatype(typ):
989            raise NtvError('ntv_value is not compatible with ntv_type')
990        return (ntv_value, ntv_name, ntv_type)

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)
909    def __init__(self, value, ntv_name=None, ntv_type=None, fast=False):
910        '''NtvSingle constructor.
911
912        *Parameters*
913
914        - **ntv_name** : String (default None) - name of the NTV entity
915        - **ntv_type**: String (default None) - type of the entity
916        - **value**: value of the entity
917        - **fast**: boolean (default False) - Ntv is created with a list of json values
918        without control
919        '''
920        if not fast:
921            value, ntv_name, ntv_type = NtvSingle._decode_s(
922                value, ntv_name, ntv_type)
923            if ntv_type and isinstance(ntv_type, str) and ntv_type[-1] == '.':
924                raise NtvError('the ntv_type is not valid')
925        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

return the json_array dynamic attribute (always False)

def obj_value(self, def_type=None, **kwargs):
948    def obj_value(self, def_type=None, **kwargs):
949        '''return the ntv_value with different formats defined by kwargs'''
950        option = {'encoded': False, 'format': 'json',
951                  'simpleval': False, 'fast': False} | kwargs
952        if option['fast'] or option['format'] in ('json', 'tuple'):
953            return self.ntv_value
954        if option['format'] == 'obj' and self.ntv_value == 'null':
955            return None
956        return NtvConnector.uncast(self, **option)[0]

return the ntv_value with different formats defined by kwargs

class NtvList(Ntv):
 993class NtvList(Ntv):
 994    '''A NTV-list entity is a Ntv entity where:
 995
 996    - ntv_value is a list of NTV entities,
 997    - ntv_type is a default type available for included NTV entities
 998
 999    *Attributes :*
1000    - no additional attributes to those of parent class `Ntv`
1001
1002    *dynamic values (@property)*
1003    - `json_array`
1004
1005    The additional methods defined in this class are :
1006
1007    *instance methods*
1008    - `obj_value`
1009    '''
1010
1011    def __init__(self, list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False):
1012        '''NtvList constructor.
1013
1014        *Parameters*
1015
1016        - **ntv_name** : String (default None) - name of the NTV entity
1017        - **ntv_type**: String (default None) - default type or namespace of
1018        the included entities
1019        - **list_ntv**: list - list of Ntv objects or obj_value of Ntv objects
1020        - **fast**: boolean (default False) - if True, Ntv is created with a list
1021        of json values without control
1022        - **type_auto**: boolean (default False) - if True, default type for NtvList
1023        is the ntv_type of the first Ntv in the ntv_value'''
1024        if isinstance(list_ntv, NtvList):
1025            ntv_value = [copy.copy(ntv) for ntv in list_ntv.ntv_value]
1026            ntv_type = list_ntv.ntv_type
1027            ntv_name = list_ntv.ntv_name
1028        elif isinstance(list_ntv, list):
1029            ntv_value = [Ntv.from_obj(ntv, ntv_type, ':', fast=fast)
1030                         for ntv in list_ntv]
1031        elif isinstance(list_ntv, dict):
1032            ntv_value = [Ntv.from_obj({key: val}, ntv_type, ':', fast=fast)
1033                         for key, val in list_ntv.items()]
1034        else:
1035            raise NtvError('ntv_value is not a list')
1036        if typ_auto and not ntv_type and len(ntv_value) > 0 and ntv_value[0].ntv_type:
1037            ntv_type = ntv_value[0].ntv_type
1038        super().__init__(ntv_value, ntv_name, ntv_type)
1039        for ntv in self:
1040            ntv.parent = self
1041
1042    @property
1043    def json_array(self):
1044        ''' return the json_array dynamic attribute'''
1045        set_name = {ntv.ntv_name for ntv in self}
1046        return '' in set_name or len(set_name) != len(self) or len(set_name)==1
1047
1048    def __eq__(self, other):
1049        ''' equal if name and value are equal'''
1050        return self.__class__.__name__ == other.__class__.__name__ and\
1051            self.ntv_name == other.ntv_name and self.ntv_value == other.ntv_value
1052
1053    def __hash__(self):
1054        '''return hash(name) + hash(value)'''
1055        return hash(self.ntv_name) + hash(tuple(self.ntv_value))
1056
1057    def __copy__(self):
1058        ''' Copy all the data '''
1059        cop = self.__class__(self)
1060        cop.parent = None
1061        return cop
1062
1063    def __setitem__(self, ind, value):
1064        ''' replace ntv_value item at the `ind` row with `value`'''
1065        if ind < 0 or ind >= len(self):
1066            raise NtvError("out of bounds")
1067        self.ntv_value[ind] = value
1068        if isinstance(value, (NtvSingle, NtvList)):
1069            value.parent = self
1070
1071    def __delitem__(self, ind):
1072        '''remove ntv_value item at the `ind` row'''
1073        if isinstance(ind, int):
1074            self.ntv_value.pop(ind)
1075        else:            
1076            self.ntv_value.pop(self.ntv_value.index(self[ind]))
1077
1078    def append(self, ntv):
1079        ''' add ntv at the end of the list of Ntv entities included'''
1080        old_parent = ntv.parent
1081        if old_parent:
1082            del(old_parent[old_parent.ntv_value.index(ntv)])
1083        self.ntv_value.append(ntv)
1084        ntv.parent = self
1085
1086    def insert(self, idx, ntv):
1087        ''' add ntv at the index idx of the list of Ntv entities included'''
1088        old_parent = ntv.parent
1089        if old_parent:
1090            del(old_parent[old_parent.ntv_value.index(ntv)])
1091        self.ntv_value.insert(idx, ntv)
1092        ntv.parent = self      
1093        
1094    def _obj_sep(self, json_name, json_type, def_type=None):
1095        ''' return separator to include in json_name'''
1096        return '::' if (json_type and json_type[-1] != '.') or (json_type and json_name) else ''
1097
1098    def obj_value(self, def_type=None, **kwargs):
1099        '''return the ntv_value with different formats defined by kwargs
1100        '''
1101        option = {'encoded': False, 'format': 'json', 'simpleval': False,
1102                  'json_array': False, 'fast': False, 'maxi': -1} | kwargs
1103        opt2 = option | {'encoded': False}
1104        maxv = len(self.ntv_value) if option['maxi'] < 1 else option['maxi']
1105        def_type = self.ntv_type.long_name if self.ntv_type else def_type
1106        values = [ntv.to_obj(def_type=def_type, **opt2)
1107                  for ntv in self.ntv_value[:maxv]]
1108        if len(values) == 1 and isinstance(values[0], dict):
1109            return values[0]
1110        if self.json_array or option['simpleval'] or option['json_array']:
1111            return values
1112        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)
1011    def __init__(self, list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False):
1012        '''NtvList constructor.
1013
1014        *Parameters*
1015
1016        - **ntv_name** : String (default None) - name of the NTV entity
1017        - **ntv_type**: String (default None) - default type or namespace of
1018        the included entities
1019        - **list_ntv**: list - list of Ntv objects or obj_value of Ntv objects
1020        - **fast**: boolean (default False) - if True, Ntv is created with a list
1021        of json values without control
1022        - **type_auto**: boolean (default False) - if True, default type for NtvList
1023        is the ntv_type of the first Ntv in the ntv_value'''
1024        if isinstance(list_ntv, NtvList):
1025            ntv_value = [copy.copy(ntv) for ntv in list_ntv.ntv_value]
1026            ntv_type = list_ntv.ntv_type
1027            ntv_name = list_ntv.ntv_name
1028        elif isinstance(list_ntv, list):
1029            ntv_value = [Ntv.from_obj(ntv, ntv_type, ':', fast=fast)
1030                         for ntv in list_ntv]
1031        elif isinstance(list_ntv, dict):
1032            ntv_value = [Ntv.from_obj({key: val}, ntv_type, ':', fast=fast)
1033                         for key, val in list_ntv.items()]
1034        else:
1035            raise NtvError('ntv_value is not a list')
1036        if typ_auto and not ntv_type and len(ntv_value) > 0 and ntv_value[0].ntv_type:
1037            ntv_type = ntv_value[0].ntv_type
1038        super().__init__(ntv_value, ntv_name, ntv_type)
1039        for ntv in self:
1040            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

return the json_array dynamic attribute

def append(self, ntv):
1078    def append(self, ntv):
1079        ''' add ntv at the end of the list of Ntv entities included'''
1080        old_parent = ntv.parent
1081        if old_parent:
1082            del(old_parent[old_parent.ntv_value.index(ntv)])
1083        self.ntv_value.append(ntv)
1084        ntv.parent = self

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

def insert(self, idx, ntv):
1086    def insert(self, idx, ntv):
1087        ''' add ntv at the index idx of the list of Ntv entities included'''
1088        old_parent = ntv.parent
1089        if old_parent:
1090            del(old_parent[old_parent.ntv_value.index(ntv)])
1091        self.ntv_value.insert(idx, ntv)
1092        ntv.parent = self      

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

def obj_value(self, def_type=None, **kwargs):
1098    def obj_value(self, def_type=None, **kwargs):
1099        '''return the ntv_value with different formats defined by kwargs
1100        '''
1101        option = {'encoded': False, 'format': 'json', 'simpleval': False,
1102                  'json_array': False, 'fast': False, 'maxi': -1} | kwargs
1103        opt2 = option | {'encoded': False}
1104        maxv = len(self.ntv_value) if option['maxi'] < 1 else option['maxi']
1105        def_type = self.ntv_type.long_name if self.ntv_type else def_type
1106        values = [ntv.to_obj(def_type=def_type, **opt2)
1107                  for ntv in self.ntv_value[:maxv]]
1108        if len(values) == 1 and isinstance(values[0], dict):
1109            return values[0]
1110        if self.json_array or option['simpleval'] or option['json_array']:
1111            return values
1112        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