NTV.json_ntv.ntv

Created on Feb 27 2023

@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.

1 - JSON-NTV structure

The NTV triplet (name, type, value) is represented using a JSON-NTV format inspired by the JSON-ND project :

  • value (if name and type are not documented)
  • { "name" : value } (if name is documented but not type)
  • { ":type" : value } for primitive entities and { "::type" : value } for structured entities (if type is documented but not name)
  • { "name:type" : value } for primitive entities and { "name::type" : value } for structured entities (if type and name are documented).

For an NTV-single, the value is the JSON-value of the entity. For an NTV-list, value is a JSON-array (JSON-object) where JSON-elements (JSON-members) are the JSON-NTV formats of included NTV entities.

This JSON-NTV format allows full compatibility with existing JSON structures:

  • a JSON-number, JSON-string or JSON-boolean is the representation of an NTV-single entity,
  • a JSON-object with a single member is the representation of an NTV-single entity
  • a JSON-array or JSON-object is the representation of an NTV-list entity

2 - Examples of JSON-NTV representations

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

@staticmethod
def from_obj_name(string):
388    @staticmethod
389    def from_obj_name(string):
390        '''return a tuple with name, type and separator from string'''
391        if not isinstance(string, str):
392            raise NtvError('a json-name have to be str')
393        if string == '':
394            return (None, None, None)
395        sep = None
396        if '::' in string:
397            sep = '::'
398        elif ':' in string:
399            sep = ':'
400        if sep is None:
401            return (string, None, None)
402        split = string.rsplit(sep, 2)
403        if len(split) == 1:
404            return (string, None, sep)
405        if split[0] == '':
406            return (None, split[1], sep)
407        if split[1] == '':
408            return (split[0], None, sep)
409        return (split[0], split[1], sep)

return a tuple with name, type and separator from string

def alike(self, ntv_value):
411    def alike(self, ntv_value):
412        ''' return a Ntv entity with same name and type.
413
414        *Parameters*
415
416        - **ntv_value**: list of ntv values'''
417        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):
419    def from_value(self):
420        '''return a Ntv entity from ntv_value'''
421        if isinstance(self.ntv_value, list):
422            return NtvList(self.ntv_value)
423        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):
425    def json_name(self, def_type=None, string=False, explicit=False):
426        '''return the JSON name of the NTV entity (json-ntv format)
427
428        *Parameters*
429
430        - **def_typ** : NtvType or Namespace (default None) - type of the parent entity
431        - **string** : boolean (default False) - If True, return a string else a tuple
432        - **explicit** : boolean (default False) - If True, type is always included'''
433        if def_type is None:
434            def_type = ''
435        elif isinstance(def_type, (NtvType, Namespace)):
436            def_type = def_type.long_name
437        json_name = self.ntv_name if self.ntv_name else ''
438        json_type = relative_type(
439            def_type, self.type_str) if self.ntv_type else ''
440        implicit = (json_type == 'json' and (not def_type or def_type == 'json') or
441                    not NtvConnector.is_json_class(self.val))
442        if implicit and not explicit:
443            json_type = ''
444        json_sep = self._obj_sep(json_type, def_type)
445        if string:
446            return json_name + json_sep + json_type
447        return [json_name, json_sep, json_type]

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

Parameters

  • def_typ : NtvType 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):
449    def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
450        '''convert NtvList entity to NtvSingle entity
451
452        *Parameters*
453
454        - **ntv_name** : String (default None) - name of the NTV entity
455        - **ntv_type**: String (default None) - type of the entity
456        - **value**: value of the entity
457        - **fast**: boolean (default False) - Ntv is created with a list of json values
458        without control
459        '''
460        return NtvSingle(self.obj_value(def_type=def_type, **kwargs),
461                         self.name if self.name else name,
462                         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):
464    def to_ntvlist(self, def_type=None, def_sep=None, no_typ=False, decode_str=False,
465                 typ_auto=False, fast=False):
466        '''convert NtvSingle entity to NtvList entity
467
468        *Parameters*
469
470        - **value**: Ntv value to convert in an Ntv entity
471        - **no_typ** : boolean (default None) - if True, NtvList is with 'json' type
472        - **def_type** : NtvType or Namespace (default None) - default type of the value
473        - **def_sep**: ':', '::' or None (default None) - default separator of the value
474        - **decode_str**: boolean (default False) - if True, string are loaded as json data
475        - **type_auto**: boolean (default False) - if True, default type for NtvList
476        is the ntv_type of the first Ntv in the ntv_value
477        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
478        '''
479        ntv = Ntv.from_obj(self.ntv_value, def_type, def_sep, no_typ, decode_str,
480                     typ_auto, fast)
481        if ntv.__class__.__name__ == 'NtvSingle':
482            return NtvList([self])
483        if self.ntv_name:
484            ntv.set_name(self.ntv_name)
485        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 : NtvType 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):
487    def notype(self):
488        '''convert NTV entity in a NV entity (with ntv_type is 'json' or None')'''
489        no_type = copy.copy(self)
490        for ntv in NtvTree(no_type).leaf_nodes:
491            ntv.set_type('json')           
492        for ntv in NtvTree(no_type).inner_nodes:
493            ntv.set_type()
494        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):
496    def reduce(self, obj=True, maxi=6, level=3):
497        '''reduce the length and the level of the entity
498        
499        *Parameters*
500
501        - **obj**: boolean (default True) - If True return jsonNTV else NTV entity
502        - **maxi**: integer (default 6) - Number of returned entities in an NtvList
503        - **level**: integer (default 6) - returned entities in an NtvList at this level is None
504        
505        *return*
506        
507        - **NTV entity** or **jsonNTV**
508        '''
509        ntv = copy.copy(self)
510        cont = Ntv.obj('...') if self.json_array else Ntv.obj({'...':''})            
511        if isinstance(self, NtvSingle):
512            return ntv
513        if level == 0:
514            ntv.ntv_value = [NtvSingle('...',ntv_type=ntv.type_str)]
515        if len(self) <= maxi:
516            ntv.ntv_value = [child.reduce(False, maxi, level-1) for child in ntv]
517            return ntv
518        mid = maxi // 2
519        cont.set_type(ntv.type_str)
520        start = [child.reduce(False, maxi, level-1) for child in ntv[:mid]]
521        #middle = [NtvSingle('...',ntv_type=ntv.type_str)]
522        middle = [cont]
523        end = [child.reduce(False, maxi, level-1) for child in ntv[-mid:]]
524        ntv.ntv_value = start + middle + end
525        if obj:
526            return ntv.to_obj()
527        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'):
529    def set_name(self, name='', nodes='simple'):
530        '''set new names to the entity
531
532        *Parameters*
533
534        - **name**: list or string (default '') - New name values
535        - **nodes**: string (default 'simple') - nodes to be changed
536            'simple': current entity
537            'leaves': NtvSingle entities
538            'inner': NtvList entities
539            'all': all entities  '''
540        match nodes:
541            case 'simple':
542                self.ntv_name = str(name)
543            case 'leaves':
544                if not isinstance(name, list):
545                    name = [str(name)] * NtvTree(self).breadth
546                for nam, ntv in zip(name, NtvTree(self).leaf_nodes):
547                    ntv.ntv_name = nam
548            case 'inner':
549                if not isinstance(name, list):
550                    name = [str(name)] * len(NtvTree(self).inner_nodes)
551                for nam, ntv in zip(name, NtvTree(self).inner_nodes):
552                    ntv.ntv_name = nam
553            case 'all':
554                if not isinstance(name, list):
555                    name = [str(name)] * NtvTree(self).size
556                for nam, ntv in zip(name, NtvTree(self).nodes):
557                    ntv.ntv_name = nam
558            case _:
559                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):
561    def set_type(self, typ=None):
562        '''set a new type to the entity
563
564        *Parameters*
565
566        - **typ**: string, NtvType, Namespace (default None)'''
567        if typ and not isinstance(typ, (str, NtvType, Namespace)):
568            raise NtvError('the type is not a valid type')
569        self.ntv_type = str_type(typ, self.__class__.__name__ == 'NtvSingle')

set a new type to the entity

Parameters

  • typ: string, NtvType, Namespace (default None)
def set_value(self, value=None, fast=False):
571    def set_value(self, value=None, fast=False):
572        '''set new ntv_value of a single entity or of a list of entities included
573
574        *Parameters*
575
576        - **value**: list or single value
577        - **fast** : boolean (default False) - if True, value is not converted'''
578        if isinstance(self, NtvSingle):
579            self.ntv_value = NtvSingle(value, ntv_type=self.ntv_type, 
580                                       fast=fast).val
581            return
582        if not isinstance(value, list):
583            value = [value] * NtvTree(self).breadth
584        ntv_val = NtvList(value, fast=fast)
585        for val, ntv in zip(ntv_val, NtvTree(self).leaf_nodes):
586            ntv.ntv_value = val.val
587        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):
589    def to_mermaid(self, title='', disp=False, row=False, leaves=False):
590        '''return a mermaid flowchart.
591
592        *Parameters*
593
594        - **title**: String (default '') - title of the flowchart
595        - **disp**: Boolean (default False) - if true, return a display else return
596        a mermaid text diagram
597        - **row**: Boolean (default False) - if True, add the node row
598        - **leaves**: Boolean (default False) - if True, add the leaf row
599        '''
600        option = {'title': title, 'disp': disp, 'row': row, 'leaves': leaves}
601        if disp:
602            Ntv.obj({':$mermaid': self.to_obj()}).to_obj(
603                format='obj', **option)
604            return None
605        return Ntv.obj({':$mermaid': self.to_obj()}).to_obj(format='obj', **option)

return a mermaid flowchart.

Parameters

  • title: String (default '') - title of the flowchart
  • disp: Boolean (default False) - if true, return a display else return a mermaid text diagram
  • row: Boolean (default False) - if True, add the node row
  • leaves: Boolean (default False) - if True, add the leaf row
def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
607    def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
608        '''return a simple json representation of the Ntv entity.
609
610        *Parameters*
611
612        - **nam**: Boolean(default True) - if true, the names are included
613        - **typ**: Boolean(default True) - if true, the types are included
614        - **val**: Boolean(default True) - if true, the values are included
615        - **jsn**: Boolean(default False) - if false, the 'json' type is not included
616        - **maxi**: Integer (default 10) - number of values to include for NtvList
617        entities. If maxi < 1 all the values are included.
618        '''
619        ntv = self.code_ntv
620        if nam and typ:
621            ntv = ntv[0]
622        if self.ntv_name and nam:
623            ntv += '-' + self.ntv_name
624        if self.ntv_type and typ and (jsn or self.ntv_type.long_name != 'json'):
625            ntv += '-' + self.ntv_type.long_name
626        clas = self.__class__.__name__
627        clas_val = self.ntv_value.__class__.__name__
628        if clas == 'NtvSingle' and clas_val != 'NtvSingle':
629            if val:
630                if ntv:
631                    ntv += '-'
632                ntv += json.dumps(self.ntv_value, cls=NtvJsonEncoder)
633            return ntv
634        if clas == 'NtvSingle' and clas_val == 'NtvSingle':
635            return {ntv:  self.ntv_value.to_repr(nam, typ, val, jsn, maxi)}
636        if clas == 'NtvList':
637            maxv = len(self.ntv_value) if maxi < 1 else maxi
638            return {ntv:  [ntvi.to_repr(nam, typ, val, jsn, maxi) 
639                           for ntvi in self.ntv_value[:maxv]]}
640        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=''):
642    def to_name(self, default=''):
643        '''return the name of the NTV entity
644
645        *Parameters*
646
647        - **default**: string (default ''): returned value if name is not present '''
648        if self.ntv_name == '':
649            return default
650        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):
652    def to_fast(self, def_type=None, **kwargs):
653        '''return the JSON representation of the NTV entity (json-ntv format)
654        without value conversion.
655
656        *Parameters*
657
658        - **def_type** : NtvType or Namespace (default None) - default type to apply
659        to the NTV entity
660        - **encoded** : boolean (default False) - choice for return format
661        (string/bytes if True, dict/list/tuple else)
662        - **format**  : string (default 'json')- choice for return format
663        (json, cbor, obj)
664        - **simpleval** : boolean (default False) - if True, only value (without
665        name and type) is included
666        - **name** : boolean (default true) - if False, name is not included
667        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
668        - **maxi**: Integer (default -1) - number of values to include for NtvList
669        entities. If maxi < 1 all the values are included.
670        '''
671        option = kwargs | {'fast': True}
672        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 : NtvType 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):
674    def to_obj(self, def_type=None, **kwargs):
675        '''return the JSON representation of the NTV entity (json-ntv format).
676
677        *Parameters*
678
679        - **def_type** : NtvType or Namespace (default None) - default type to apply
680        to the NTV entity
681        - **encoded** : boolean (default False) - choice for return format
682        (string/bytes if True, dict/list/tuple else)
683        - **format**  : string (default 'json')- choice for return format
684        (json, cbor, obj)
685        - **simpleval** : boolean (default False) - if True, only value (without
686        name and type) is included
687        - **name** : boolean (default true) - if False, name is not included
688        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
689        - **fast** : boolean (default False) - if True, json is created without conversion
690        - **maxi**: Integer (default -1) - number of values to include for NtvList
691        entities. If maxi < 1 all the values are included.
692        '''
693        option = {'encoded': False, 'format': 'json', 'fast': False, 'maxi': -1,
694                  'simpleval': False, 'name': True, 'json_array': False} | kwargs
695        value = self.obj_value(def_type=def_type, **option)
696        obj_name = self.json_name(def_type)
697        if not option['name']:
698            obj_name[0] = ''
699        if option['simpleval']:
700            name = ''
701        elif option['format'] in ('cbor', 'obj') and not NtvConnector.is_json_class(value):
702            name = obj_name[0]
703        else:
704            name = obj_name[0] + obj_name[1] + obj_name[2]
705        json_obj = {name: value} if name else value
706        if option['encoded'] and option['format'] == 'json':
707            return json.dumps(json_obj, cls=NtvJsonEncoder)
708        if option['encoded'] and option['format'] == 'cbor':
709            return NtvConnector.connector()['CborConnec'].to_obj_ntv(json_obj)
710        return json_obj

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

Parameters

  • def_type : NtvType 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):
712    @staticmethod
713    def obj_ntv(value, name='', typ='', single=False):
714        '''return a json-ntv representation without using Ntv structure.
715
716        *Parameters*
717
718        - **value** : ntv-value of the json-ntv
719        - **name** : string (default '') - ntv-name of the json-ntv
720        - **typ** : string (default '') - ntv_type of the json-ntv
721        - **single** : boolean (default False) - if True, value is a single object
722        else value is a set of objetcs.
723        '''
724        value = {} if not value else value
725        name = '' if not name else name
726        typ = '' if not typ else typ
727        ntv_list = len(value) != 1 if isinstance(
728            value, dict) else isinstance(value, list)
729        if not single and not ntv_list:
730            raise NtvError('value is not compatible with not single NTV data')
731        sep = ':' if single else '::'
732        sep = '' if not typ and (not single or single and not ntv_list) else sep
733        name += sep + typ
734        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):
736    def to_json_ntv(self):
737        ''' create a copy where ntv-value of the self-tree nodes is converted 
738        in json-value'''
739        ntv = copy.copy(self)
740        for leaf in ntv.tree.leaf_nodes:
741            if isinstance(leaf.ntv_value, (NtvSingle, NtvList)):
742                leaf.ntv_value = leaf.ntv_value.to_obj()
743                leaf.ntv_type = NtvType('ntv')
744            elif not leaf.is_json:
745                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.cast(
746                    leaf.ntv_value, leaf.ntv_name, leaf.type_str)
747                leaf.ntv_type = NtvType.add(type_str)
748                leaf.is_json = True
749        return ntv

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

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

replace self by ntv in the tree

tree

return a tree with included entities (NtvTree object)

@abstractmethod
def obj_value(self):
833    @abstractmethod
834    def obj_value(self):
835        '''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):
864    @staticmethod
865    def decode_json(json_value):
866        '''return (value, name, type, separator, isjson) of a json object'''
867        if isinstance(json_value, dict) and len(json_value) == 1:
868            json_name = list(json_value.keys())[0]
869            val = json_value[json_name]
870            return (val, *Ntv.from_obj_name(json_name), NtvConnector.is_json(val))
871        return (json_value, None, None, None, NtvConnector.is_json(json_value))

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

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

return the ntv_value with different formats defined by kwargs

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

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

def insert(self, idx, ntv):
1092    def insert(self, idx, ntv):
1093        ''' add ntv at the index idx of the list of Ntv entities included'''
1094        old_parent = ntv.parent
1095        if old_parent:
1096            del(old_parent[old_parent.ntv_value.index(ntv)])
1097        self.ntv_value.insert(idx, ntv)
1098        ntv.parent = self      

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

def obj_value(self, def_type=None, **kwargs):
1106    def obj_value(self, def_type=None, **kwargs):
1107        '''return the ntv_value with different formats defined by kwargs
1108        '''
1109        option = {'encoded': False, 'format': 'json', 'simpleval': False,
1110                  'json_array': False, 'fast': False, 'maxi': -1} | kwargs
1111        opt2 = option | {'encoded': False}
1112        maxv = len(self.ntv_value) if option['maxi'] < 1 else option['maxi']
1113        def_type = self.ntv_type.long_name if self.ntv_type else def_type
1114        if self.json_array or option['simpleval'] or option['json_array']:
1115            return [ntv.to_obj(def_type=def_type, **opt2) for ntv in self.ntv_value[:maxv]]
1116        values = [ntv.to_obj(def_type=def_type, **opt2)
1117                  for ntv in self.ntv_value[:maxv]]
1118        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