21. Interactie tussen Python en Mumps via JSON structuren

21.1. Inleiding : JSON als gegevensformaat

JSON of Javascript Object Notation, is een gestandaardiseerd gegevensformaat, met een aantal mooie eigenschappen :

  • JSON is voor de mens leesbare tekst.
  • JSON is compact.
  • JSON heeft een hierarchische structuur.
  • JSON is taalonafhankelijk.
  • De interpretatie van de karakters ligt vast.
  • Er bestaan in diverse programmeertalen tools om JSON te lezen en aan te maken.

Dit heeft ertoe geleid, dat ook binnen Brocade dit wordt gebruikt:

  • als uitwisselingsformaat tussen de M omgeving en andere programmeertalen, zoals Python, LUA, ..
  • als exportformaat
  • als dataformaat, om gestructureerde data weg te schrijven in globals

21.2. MJSON : een gegevensformaat in en buiten M

Hoewel zowel globals als JSON een hierarchische structuur kunnen herbergen, toch kan men niet zomaar eender welke global structuur mappen in JSON en vice versa. Een voorbeeld : zo kan men in een global data wegschrijven ter hoogte van een node, terwijl deze node ook nog onderliggende niveau’s bevat ($data = 11). Dit heeft niet direct een tegenhanger in JSON.

We willen ervoor zorgen, dat je een eenduidige stuctuur hebt, die zowel een M global als een JSON representatie heeft. Dat kan je op twee manieren benaderen :

  • Je ontwerpt een vernuftige, en dus complexe structuur.
  • OF : Je legt voorwaarden op aan je structuur.

En wij hebben voor dit laatste gekozen : de MJSON structuur.

21.2.1. MJSON : Definitie

Een MJSON structuur is een JSON structuur, met extra voorwaarden :

  • Values zijn steeds strings.

    In M bestaat er slechts deze vorm van values, vandaar een logische keuze om de eenduidigheid te bewaren. Dit betekent :

    Getallen zijn uitgesloten als values : In M bestaat het verschil niet tussen de string "1" en het getal 1. Je stelt een getal gewoon voor door zijn overeenkomstige string.

    De waarden null, true en false komen nooit voor : in M bestaan geen specifieke booleaanse waarden. Vervang desgewenst een true waarde door 1, en false door 0. De waarde null gebruiken we gewoon niet.

  • Lege (onder)structuren, zoals [] en {} zijn uitgesloten.

    Het zou een te complexe opzet worden, indien je in M een lege array zou moeten herkennen. Dus we sluiten zulke structuur gewoon uit. Zonder lege arrays kunnen we leven.

  • Bij name/value paren mag name tevens niet gelijk zijn aan de lege string.

    Dit is een logisch gevolg van het feit, dat in M geen lege string mag voorkomen als subscripts.

  • Er komen geen leesdelimiters voor, zoals linefeeds, ...

    Hoewel we deze extra voorwaarde niet per se in ons verlanglijstje hoefden, toch heeft dit zo zijn voordelen, voornamelijk bij het parsen van streams van MJSON structuren. Het vormt ook geen grote belemmering voor een opzet van MJSON structuren.

Opmerkingen :
  • Bemerk dat voorgaande voorwaarden een vrij kleine beperking leggen op de mogelijkheden binnen MJSON. Ze zijn ook stuk voor stuk een logisch gevolg van ons uitgangspunt, dat je ze eenduidig en eenvoudig in M kan mappen. Deze extra voorwaarden vormen in de praktijk geen grote struikelblok.

  • MSJON structuren zijn typische structuren, die in het interne geheugen van een computer passen : ze worden bvb. eerder gebruikt voor de data van een catalografisch record, dan voor een lijst van catalografische records, of een regelwerk in zijn geheel. In M termen betekent dit : we gaan ervan uit, dat een MJSON structuur past in een lokale array. Is er dan geen gevaar voor de beruchte <STORE> fouten, waarbij de toegewezen partitie van een M job volloopt ?

    • Een extreem voorbeeld : record c:vabb:355635 bevat verwijzingen naar 3.100 (!) auteurs : de grootte van de representerende MSJON string is amper 36Kb, een fractie van de kleinste standaard partitie grootte van een Brocade productie server : (16Mb, Cache release 2010.2)
    • Een tweede voorbeeld : record c:lvd:215126 bevat meer dan 4.000 relaties : de representerede MSJON string is net geen 600Kb.
    • Voor alle Brocade systemen, draaiend op GT.M of op Cache vanaf release 2012.2, wordt de partitie grootte dynamisch toegekend, en is ze zo goed als beperkt door het onderliggende besturingssysteem. Het verdient aanbeveling om de configuratieparameters van het M systeem bij te sturen.

Voorbeelden van geldige MJSON structuren :

['spam', 'eggs','spam']
['spam', 'eggs','spam', "true", ""]
{"name":"Jos", "gender":"M", class:""}
{['spam', 'eggs','spam'], {"name":"Jos","gender":"M"}}

Voorbeelden van ongeldige MJSON structuren, die wel geldige JSON structuren zijn:

[]
[[]]
{[], {}, "A"}
['spam', 'eggs','spam', null]
['spam', 'eggs','spam', true, ""]
{"name":"Jos", "":"M"}
{['spam', 'eggs','spam'], {"name":"Jos","gender":"M", "":"M"}}

21.2.2. Mapping van M in MJSON structuren

  • JSON strings zijn M strings in de Brocade karakterset. Voorbeeld :

    GT.M>s RDx="L'«0153»uvre au noir"
    GT.M>m4_writeJSON(RDx)
    
    "L'\u0153uvre au noir"
    
  • JSON objects worden omgezet in de overeenkomstige M structuur. Voorbeeld :

    GTM>k RA
    GTM>s RA("a")="Spam"
    GTM>s RA("b")="Eggs"
    GT.M>m4_writeJSON(RA)
    
    {"a":"Spam","b":"Eggs"}
    
  • JSON Arrays komen overeen met M structuren, waarbij de index de volgorde bepaalt, de data in het rechterlid staat, en waarbij de letter “L” wordt genoteerd op het hoogste niveau. Voorbeeld :

    GT.M>k RA
    GT.M>s RA="L"
    GT.M>s RA(1)="Spam"
    GT.M>s RA(2)="Eggs"
    GT.M>m4_writeJSON(RA)
    
    ["Spam","Eggs"]
    
  • Maar het volgende levert dezelfde JSON op:

    GT.M>k RA
    GT.M>s RA="L"
    GT.M>s RA("a")="Spam"
    GT.M>s RA("b")="Eggs"
    GT.M>m4_writeJSON(RA)
    
    ["Spam","Eggs"]
    
  • Uiteraard kunnen zulke structuren genesteld zijn. Voorbeeld :

    GT.M>k RA
    GT.M>s RA("au")="L"
    GT.M>s RA("au",1)="aut^0^oip^Claessen^C.^^"
    GT.M>s RA("au",2)="aut^1^oip^a::920.12604:1.1^^^"
    GT.M>s RA("im")="L"
    GT.M>s RA("im",1)="^0^^YYYY^1991^^^^^^0^^oip^^"
    GT.M>s RA("lm","abua")=""
    GT.M>s RA("lm","irua")=""
    GT.M>s RA("opac","cat.irua","*")=""
    GT.M>s RA("su","a::irc.16:1")=""
    GT.M>s RA("su","a::pt.125:1")=""
    GT.M>m4_writeJSON(RA)
    
    {"au":["aut^0^oip^Claessen^C.^^","aut^1^oip^a::920.12604:1.1^^^"],"im":["^0^^YYYY^1991^^^^^^0^^oip^^"],"lm":{"abua":"","irua":""},"opac":{"cat.irua":{"*":""}},"su":{"a::irc.16:1":"","a::pt.125:1":""}}
    

21.2.3. Mapping van Python in MJSON structuren

Hier gebeurt de mapping op een voor de hand liggende wijze, via de standaard module json, beschikbaar in Python, vanaf release 2.60 :

>>> import json
>>> x = '{"au":["aut^0^oip^Claessen^C.^^","aut^1^oip^a::920.12604:1.1^^^"],"im":["^0^^YYYY^1991^^^^^^0^^oip^^"],"lm":{"abua":"","irua":""},"opac":{"cat.irua":{"*":""}},"su":{"a::irc.16:1":"","a::pt.125:1":""}}'
>>> y = json.loads(x)
>>> y

{u'im': [u'^0^^YYYY^1991^^^^^^0^^oip^^'], u'lm': {u'irua': u'', u'abua': u''}, u'au': [u'aut^0^oip^Claessen^C.^^', u'aut^1^oip^a::920.12604:1.1^^^'], u'opac': {u'cat.irua': {u'*': u''}}, u'su': {u'a::pt.125:1': u'', u'a::irc.16:1': u''}}

Bemerk dat dit unicode strings zijn.

21.2.4. Beperkingen op de M structuur

Niet alle M structuren kunnen worden omgezet naar MSJON : de beperkingen erop vallen echter best mee, en zijn een rechtstreeks gevolg van onze keuze voor een eenvoudige mapping :

De M structuur moet kunnen gelezen worden in een local array.
Dit is een evidente voorwaarde, maar in de praktijk zijn hier nauwelijks problemen te verwachten.
Een M structuur bevat op elk niveau nooit tegelijk relevante data en onderliggende data : ( $d()=1 of $d()=10 ,maar nooit 11).

Zowat alle global structuren binnen Brocade voldoen hieraan, en voor de anderen kan men eenvoudige oplossingen bedenken.

  • In menselijke taal heet dit : volgende M structuur kan niet behandeld worden :

    RA="data"
    RA("x")="Meer data"
    RA("y")="Nog meer data"
    
  • In technische termen heet dat : Voor elke referentie x geldt: als $d(@x)>1, dan bevat $g(@x) geen relevante data.

Voorbeeld van een geldige MJSON M structuur :

RA("x")="L"
RA("x",1)="Spam"
RA("x",2)="Eggs"
RA("x",3,"after")="Half Eggs"
RA("x",3,"before")="Half Spam"

Voorbeeld van een ongeldige MJSON M structuur :

RA("x")="L"
RA("x",1)="Spam"
RA("x",2)="Eggs"
RA("x",3)="Our Menu"
RA("x",3,"after")="Half Eggs"
RA("x",3,"before")="Half Spam"

21.3. Schrijven/Lezen van MJSON structuren

21.3.1. Schrijven/Lezen van JSON structuren in de M omgeving

In de M omgeving maak je gebruik van volgende macro’s om conversies te organiseren tussen JSON strings en MJSON structuren :

macro dumpMJSON($return, $mumpsref, $anetsource=r4_m_anetsource, $asciitarget=1, $forcetype="", $overflow=""):
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  produceer een JSON structuur a.h.v. een MJSON global referentie
    $return: Naam van de variabele, die de JSON structuur achteraf bevat.
    $mumpsref: naam van de M structuur, die de om te zetten MJSON structuur bevat. Mumps variabele of referentie. (global of local array)
    $anetsource: Optioneel. 1 = $mumpsref is in Anet encoding | 0 = $mumpsref is in UTF-8 encoding
    $asciitarget: Optioneel. 1 = json is in ASCII encoding | 0 = json is in UTF-8 encoding
    $forcetype: Optioneel. Heeft enkel effect op het hoogste niveau.
                "" : de JSON codering wordt bepaald door de waarde in $mumpsref
                "L" : de JSON codering is een list, ongeacht de waarde van @$mumpsref
                Anders, dan is de JSON codering een dict, ongeacht de waarde van @$mumpsref
    $overflow: Optioneel. Enkel van belang, als de JSON structuur niet in 1 M string past. Naam van de variabele, waarin een global referentie terechtkomt, die de (complete) JSON structuur bevat ; Deze is van de vorm (i)=JSON . Leeg indien geen overflow. Opmerking : subscript (0) = $return
    $example: m4_dumpMJSON(RDjson,RA)
    $example: m4_dumpMJSON(RDjson,RA,kill=0,anetsource=0)
    $example: m4_dumpMJSON(RDjson,RA,kill=0,anetsource=0,asciitarget=0)
    $example: m4_dumpMJSON(RDjson,RA,kill=0,anetsource=0,forcetype="",asciitarget=0)
    $example: m4_dumpMJSON(json,RAlibs(1),asciitarget=1,overflow=RGjsonr)
    $example: m4_dumpMJSON(json,^ZTEST("aa"),asciitarget=1,overflow=RGjsonr)
    '''

    «s MDx=$na($mumpsref),$return=$$%Dump^stdjsdp(MDx,$anetsource,$asciitarget,$forcetype,.$overflow)»
macro writeMJSON($mumpsref, $anetsource=r4_m_anetsource, $asciitarget=1, $forcetype="", $file="", $error=UDioerr, $key=""):
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  schrijft een MJSON structuur neer in JSON formaat in een bestand
    $mumpsref: referentie M structuur. Bevat de naam van de om te zetten MJSON structuur.
               Zowel globals als local arrays zijn welkom.
    $anetsource: Optioneel. 1 = $mumpsref is in Anet encoding | 0 = $mumpsref is in UTF-8 encoding
    $asciitarget: Optioneel. 1 = json is in ASCII encoding | 0 = json is in UTF-8 encoding
    $forcetype: Optioneel. Deze parameter heeft enkel effect op het hoogste niveau.
                   "" : de JSON codering wordt bepaald door de waarde in $mumpsref
                   "L" : de JSON codering is een list, ongeacht de waarde van @$mumpsref
                   Anders, dan is de JSON codering een dict, ongeacht de waarde van @$mumpsref
    $file: bestandsnaam, Indien leeg, dan wordt er op stdout geschreven.
           bestaat het bestand reeds, dan wordt het overschreven.
           Het bestand wordt ook afgesloten
    $error: Optioneel. Naam van de variabele, die een foutboodschap bevat. 0: correct weggeschreven | fout melding
    $key: Optioneel. Laat de MJSON structuur voorafgaan aan "$key": in JSON formaat. Doet dit enkel als $key niet leeg is.
    $example: m4_writeMJSON(^BCAT("irua",10), file="/library/tmp/rphilips.json")
    $example: m4_writeMJSON(RA)
    $example: m4_writeMJSON(RA,key="c:lvd:6789")
    '''

    «s MDr=$na($mumpsref) s $error=$$%Write^stdjsdp(MDr,$anetsource,$asciitarget,$forcetype,$file,$key)»
macro loadMJSON($glvn, $json, $pointer=MDpoint, $writetypes=1, $kill=1, $anetsource=r4_m_anetsource, $anettarget=r4_m_anetsource, $keys=MAkeys, $context="", $lg="", $store="", $index="", $default=MDdef):
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  parse een (deel van een) JSON string, en zet om naar eem Mumps structuur
    $glvn: naam van de M variabele, waarin de structuur terecht komt.
    $json: naam van de variabele, die de JSON structuur bevat. = JSON string(indien $d()=0) EN eventueel (i)=extra JSON string delen
    $pointer: pointer array ; bij een volledige JSON is pointer=0 na verwerking ; bevat de statusinformatie bij onvolledige JSON structuren. Je geeft deze pointer door aan de volgende load bij onvolledige verwerking.
    $writetypes: optioneel. Worden de types JSON types structuren bewaard in M.
                 2 = in global. (lijsten worden aangeduid met "L", de rest is as is)
                 1 = in global, maar niet op het hoogste niveau
                 0 = types niet wegschrijven.
    $kill: moet de M variabale glvn voorafgaandelijk worden gekilled.
    $anetsource: Optioneel. 1 = json is in Anet encoding | 0 = json is in UTF-8 encoding
    $anettarget: Optioneel. 1 = mumps is in Anet encoding | 0 = mumps is in UTF-8 encoding
    $keys: Sleutelverzameling
    $context: b (booleaans) | m (math) | anders
    $lg: Dialoogtaal
    $store: verzameling van templates
    $index: indexwaarde voor meervoudige sleutels
    $default: Default waarde bij berekeningen. kan ook niet gedefinieerd zijn
    $example: s json="{""city"":""New York"",""postalCode"":""10021""}" m4_loadMJSON(RAm,json,RAstp)
    $example: s json="{""FDform"":""myform"",""FDarr_1_lst"":[ 100, 120]}" m4_loadMJSON(RAm,json,RAstp, writetypes=0)
    $example: s json="{""form"":form}",RApar("form")="myform" m4_loadMJSON(RAm,json,RAstp, keys=RApar)
    '''

    «k MDpoint,MDdef,MAkeys s MDcom="pragma set $glvn" s MDx=$na($glvn),MDy=$na($json) d %Load^stdjsld(MDy,MDx,$writetypes,.$pointer,$anetsource,$anettarget,$kill,.$keys,$context,$lg,$store,$index,$g($default))»
macro loadMJSONForm($json, $pointer, $writetypes=0, $anetsource=r4_m_anetsource, $anettarget=r4_m_anetsource, $trash, $expand=1):
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  parse een (deel van een) JSON string, afkomstig van een Brocade form  en zet om naar een Mumps structuur. De nodes op het eerste niveau van de JSON structuur worden gezien als namen van variabelen, die zullen worden gezet.
    $json: naam van de variabele, die de JSON structuur bevat. = JSON string(indien $d()=0) EN eventueel (i)=extra JSON string delen
    $pointer: pointer array ; bij een volledige JSON is pointer=0 na verwerking ; bevat de statusinformatie bij onvolledige JSON structuren. Je geeft deze pointer door aan de volgende load bij onvolledige verwerking.
    $writetypes: optioneel. Worden de types JSON types structuren bewaard in M.
                 1 = in global, maar niet op het hoogste niveau
                 0 = types niet wegschrijven.
                 (optie 2 is hier niet van toepassing)
    $anetsource: Optioneel. 1 = json is in Anet encoding | 0 = json is in UTF-8 encoding
    $anettarget: Optioneel. 1 = mumps is in Anet encoding | 0 = mumps is in UTF-8 encoding
    $trash: Optioneel. Bevat de naam van de M array, die de onverwerkte nodes bevat. **Deze array wordt aangevuld**
    $expand: expandeer/clean de inhoud van de velden 1(default)=ja 0=nee
    $example: s json="{""FDform"":""myform"",""FDarr_1_lst"":[ 100, 120]}" m4_loadMJSONForm(json,RAstp, writetypes=0)
    $example: s json="{""FDform"":""myform"",""FDarr_1_lst"":[ 100, 120],""_FDid_var"":""Under""}" m4_loadMJSONForm(json,RAstp,writetypes=0,trash=RAtrash)
    '''

    «d %LoadF^stdjsld(.$json,$writetypes,.$pointer,$anetsource,$anettarget,.$trash,$expand)»
macro encJSONString($json, $mumps, $anetsource=r4_m_anetsource, $asciitarget=1, $delimit=0):
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  Codeer een string volgens de JSON specificatie
    $json: string gecodeerd volgens de JSON specificatie
    $mumps: Brocade string
    $anetsource: 0 = $mumps is in UTF-8 encoding | 1 = $mumps is in Anet encoding
    $asciitarget: 1 = $json is in ASCII encoding | 0 = $json is in UTF-8 encoding
    $delimit: plaats voor en na het resultaat een "
    $example: m4_encJSONString(json, "Een Brocade string")
    '''

    «s $json=$$%EncJSON^stdsjson($mumps,$anetsource,$asciitarget,$delimit)»
macro decJSONString($mumps, $json, $anetsource=r4_m_anetsource, $anettarget=r4_m_anetsource):
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  Decodeer een string die gecodeerd is volgens de JSON specificatie
    $mumps: Brocade string
    $json: string gecodeerd volgens de JSON specificatie.
           Indien de string begint met een " dan wordt het eerste en laatse karakter verwijderd.
    $anetsource: 0 = $json is in UTF-8 encoding | 1 = $json is in Anet encoding
    $anettarget: 0 = $mumps is in UTF-8 encoding | 1 = $mumps is in Anet encoding
    $example: m4_decJSONString(brocade, "Een JSON \t string")
    $example: m4_decJSONString(brocade, "Een JSON \t string", anetsource=1)
    '''

    «s $mumps=$$%DecJSON^stdsjson($json,$anetsource,$anettarget)»

21.3.2. Schrijven/Lezen van JSON structuren in de Python omgeving

In de Python omgeving is vanaf release 2.6 een standaard module json voorzien. Uiteraard kunnen hiermee ook MJSON structuren worden omgezet.

Voor verdere uitleg verwijzen we graag naar de Python documentatie.

21.3.3. Het sturen van MJSON van M naar Python : spawner.catchMJSON

We illustreren het gebruik van spawner.catchMJSON met behulp van een voorbeeld.

  • Maak een M procedure, die een MJSON array opbouwt, en ze naar buiten brengt :

    def %Tst:
     n x,RAm
     s RAm="L"
     s RAm(1,"a")="value a"
     s RAm(2,"b")="value b"
     s RAm(3,"c","ca")="subvalue ca"
     s RAm(3,"c","cb")="subvalue cb"
     m4_writeMJSON(RAm)
     q
    

Hierbij kan je dankbaar gebruik maken van macro m4_writeMJSON(RAm) .

  • Om in Python deze MJSON structuur in M array RA te bekomen , volstaat het volgende:

    >>> from anet.core import spawner
    >>> L = spawner.catchMJSON('d %Tst^zmyrou')
    >>> print(L)  # type(L) -> <type 'list'>
    
    [{u'a': u'value a'}, {u'b': u'value b'}, {u'c': {u'cb': u'subvalue cb', u'ca': u'subvalue ca'}}]
    

Een voorbeeld uit de Brocade wereld vind je hier :

>>> from anet.core import spawner
>>> x = spawner.catchMJSON('d %MJSON^bcmcgen("c:lvd:438","oclc")')
>>> print(x)

{ u'lg': [{u'lg': u'spa', u'ty': u'dt'}]
, u'sy': {u'gmd': u''}
, u'co': [{u'pr': u'oip', u'sz': u'', u'ka': u'', u'yr': u'', u'pg': u'271 p.', u'ty': u'normal', u'fm': u'', u'bp': u'', u'il': u'', u'so': u'nd', u'nr': u'', u'vo': u'', u'ep': u'', u'if': u''}]
, u'ss': {u'md': u'62060,53680', u'ss': u'', u'tp': u'', u'st': u'', u'cd': u'53310,43905', u'mp': u'avigne', u'td': u'', u'cp': u'UA-CDE'}
, u'lm': {u'zebra': u''}
, u'cloi': u'c:lvd:438'
, u'ti': [{u'pr': u'oip', u'lg': u'spa', u'ac': u'0', u'ty': u'h', u'ap': u'5', u'so': u'fp', u'ex': u'', u'ti': u'Las mocedades del Cid'}, {u'pr': u'oip', u'lg': u'mul', u'ac': u'a::8.860.68:1', u'ty': u'u', u'ap': u'1', u'so': u'ext', u'ex': u'', u'ti': u'Rodrigo y el Rey Don Fernando'}]
, u're': {u'vnr': {u'   15': {u'c:lvd:212751': u'Cl\xe1sicos castellanos'}}}
, u'im': [{u'pr': u'oip', u'ju2dv': u'', u'fu': u'pbl', u'ty': u'normal', u'ju2sv': u'', u'ju1dv': u'', u'ju1ty': u'YYYY', u'pc': u'0', u'so': u'nd', u'uc': u'0', u'ju1sv': u'1968', u'ju2ty': u'YYYY', u'ug': u'Espasa-Calpe', u'pl': u'Madrid', u'pso': u'nd'}]
, u'tk': u'0'
, u'pk': {u'UA-CST': {u'p:lvd:1005': {u'ab': u'', u'tx': u'', u'ty': u'mag-ow2', u'an': u'', u're': u'', u'im': u'', u'uc': u'', u'aw': u'', u'rc': u'', u'pk': u'MAG-OW-AA 30477', u'ic': u'', u'du': u'', u'id': u'', u'bz': u'', u'-vo': {u'-': {u'nt': u'', u'bc': {u'o:lvd:1227': {u'lndate': u'', u'ip': u'1', u'is': u'UA-CST', u'ani': u'', u'vo': u'-', u'mtype': u'', u'aw': u'-', u'cu': u'', u'rp': u'', u'rec': u'c:lvd:438', u'an': u'', u'-ind': {u'03030158531': u'bc'}, u'bi': u'', u'lnaction': u'', u'mag': u'', u'pk': u'p:lvd:1005', u'dt': u'58439,', u'cd': u'', u'lnwks': u'', u'up': u'UA-N', u'lntrans': u'', u'sg': u''}}}}}}}
, u'ow': {u'a::pt.41:1': u'PT.Tekstuitgave', u'a::860:1': u'U.Spaanse letterkunde'}
, u'au': [{u'pr': u'oip', u'ac': u'0', u'fu': u'aut', u'vn': u'Guill\xe9n', u'so': u'nd', u'ex': u'', u'fn': u'Castro y Bellvis'}, {u'pr': u'oip', u'ac': u'0', u'fu': u'edt', u'vn': u'Victor', u'so': u'nd', u'ex': u'', u'fn': u'Said Armesto'}]
, u'nr': [{u'pr': u'oip', u'ch': u'0', u'ty': u'co', u'so': u'nd', u'ex': u'', u'nr': u'1.527'}]
, u'dr': {u'paper': u''}}

>>> print(x['ti'])

[{u'pr': u'oip', u'lg': u'spa', u'ac': u'0', u'ty': u'h', u'ap': u'5', u'so': u'fp', u'ex': u'', u'ti': u'Las mocedades del Cid'}, {u'pr': u'oip', u'lg': u'mul', u'ac': u'a::8.860.68:1', u'ty': u'u', u'ap': u'1', u'so': u'ext', u'ex': u'', u'ti': u'Rodrigo y el Rey Don Fernando'}]

>>> x['pk']['UA-CST']['p:lvd:1005']['pk']

u'MAG-OW-AA 30477'

Hierbij geef je een M routine %MJSON^bcmcgen twee parameters mee : de identiteit van het catalografisch record (c:lvd:438) en de naam van de catalografische export generator (oclc)

21.3.4. Het sturen van MJSON van Python naar M : het ('mjson') element

In de spawner interface kan je in M instructies nu ook gebruik maken van de 'mjson' constructie. Een voorbeeld maakt alles duidelijk:

>>> import sys
>>> from anet.core import spawner
>>> myobj = ['spam', 'eggs', {"coffee":"black"}]
>>> miterable = [
        ('mjson', 'RA2', myobj),
        ('exec', 'zwrite RA2')
        ]
>>> spawner.feedMumps(iterable=miterable, stdout=sys.stdout)

GTM>
RA2="L"
RA2(1)="spam"
RA2(2)="eggs"
RA2(3)=""
RA2(3,"coffee")="black"
GTM>

21.4. MJSON sets : iteraties van MJSON structuren

In de praktijk bestaat er bestaat algauw de nood om niet alleen een individuele structuur te exporteren, maar ook een reeks van zulke structuren te verwerken, en telkens een operatie uit te voeren op een element ervan. Je wil bvb. niet alleen een catalografisch record exporteren, maar ook een lijst van catalografische records, en per record een bepaalde handeling uitvoeren.

21.4.1. MJSON set : de Definitie

MJSON structuren kunnen ook voorkomen in itererende vorm : dit noemen wij een MJSON set. Een MJSON set is een verzameling MJSON structuren, en vormt op haar beurt ook een JSON structuur, zij het met extra voorwaarden.

In tegenstelling tot de ‘beperkte’ omvang van een MJSON structuur, kunnen MJSON sets ‘immens groot’ zijn. Vergelijk dit grosso modo met de grootte van een record versus de grootte van een selectie uit een databank.

Er bestaan twee vormen :

21.4.2. MJSON sets in list formaat

Een MJSON set in list formaat is een JSON structuur, die voldoet aan volgende voorwaarden :

  • Een MJSON set bestaat uit ‘lijnen’ met karakters.
  • De ‘lijnen’ zijn gescheiden door een linefeed.
  • De eerste lijn bevat enkel een [, de laatste lijn bevat een ] .
  • Alle andere lijnen bevatten MJSON structuren, gevolgd door een ,.
  • De voorlaatste lijn eindigt niet op een ,.
  • Op die manier is een MJSON set ook een geldige JSON structuur.

Voorbeeld :

[
['spam', 'eggs','spam']
['spam', 'eggs','spam', "true", ""]
{"name":"Jos", "gender":"M", class:""}
{['spam', 'eggs','spam'], {"name":"Jos","gender":"M"}}
]

21.4.3. MJSON sets in dict formaat

Een MJSON set in dict formaat is een JSON structuur, die voldoet aan volgende voorwaarden :

  • De ‘lijnen’ zijn gescheiden door een linefeed.
  • De eerste lijn bevat enkel een {, de laatste lijn bevat een } .
  • Alle andere lijnen bevatten een JSON string + : + een MJSON structuur, + ,
  • De voorlaatste lijn eindigt niet op een ,.
  • Op die manier is een MJSON set ook een geldige JSON structuur.

Voorbeeld :

{
"a" : ['spam', 'eggs','spam'],
"b" : ['spam', 'eggs','spam', "true", ""],
"c" : {"name":"Jos", "gender":"M", class:""},
"\u0153uvre" : {['spam', 'eggs','spam'], {"name":"Jos","gender":"M"}}
}

21.5. Het genereren van MJSON sets

21.5.1. MJSON sets genereren vanuit M

Voorbeeld van m code, die de inhoud van een Brocade lijst genereert in een MJSON set (zie /stdlib/json/stdemo.m ):

def %TstLst(PDlst):
 n RDlst,ulst,xx
 m4_stdListName(ulst,PDlst)
 m4_getListInode(RDlst, ulst)
 m4_writeMJSONStart(stype="D")
 m4_loopList(RDlst, "TstLst1(RDnode,.RAattrib)")
 m4_writeMJSONEnd
 q

def TstLst1(PDnode,PAattrib):
 n ZAtt,xx
 ;pragma fos RDlst
 m4_getListNodeListAttributes(ZAtt,RDlst,PDnode)
 m4_writeMJSON(ZAtt,forcetype="O",key=PDnode)
 q

21.5.2. MJSON sets genereren vanuit Python : spawner.genMJSONlist en spawner.genMJSONdict .

Voorbeeld van Python code, die de inhoud van een itereerbare Python structuur genereert in een MJSON set :

>>> from anet.core import spawner

>>> alld = {}
>>> alld['a'] = ['spam a', 'eggs a']
>>> alld['b'] = ['spam b', 'eggs b']
>>> alld['c'] = ['spam c', 'eggs c']
>>> alld['d'] = {'spam':'spam d', 'eggs':'eggs d'}

>>> for line in spawner.genMJSONlist(alld.values()):
        print(line)

       [
["spam a", "eggs a"]
,["spam c", "eggs c"]
,["spam b", "eggs b"]
,{"eggs": "eggs d", "spam": "spam d"}
]

>>> for line in spawner.genMJSONdict(alld.iteritems()):
        print(line)

{
"a":["spam a", "eggs a"]
,"c":["spam c", "eggs c"]
,"b":["spam b", "eggs b"]
,"d":{"eggs": "eggs d", "spam": "spam d"}
}

21.6. Het sturen van MJSON sets van M naar Python : spawner.iterMJSON .

Hier vind je we dan een antwoord op de inleidende vraag, om in Python te beschikken over een iterator van MJSON structuren, die vanuit de M omgeving afkomstig zijn. We illustreren met een voorbeeld, waarbij je telkens een lijst uit Brocade doorloopt, en de informatie per lijst node aanbiedt als MJSON object in Python.

Wat moet je zelf aanmaken ?
Het enige, wat je zelf moet aanmaken, is een stuk M code, die een MJSON set genereert op de output. Voorbeeld :
def %Lst(PDlst):
 ;vb: d %Lst^stdemojs("lst:6635861")
 n RDlst,ulst,xx
 m4_stdListName(ulst,PDlst)
 m4_getListInode(RDlst, ulst)
 m4_writeMJSONStart(stype="D")
 m4_loopList(RDlst, "Lst1(RDnode,.RAattrib)")
 m4_writeMJSONEnd
 q

def Lst1(PDnode,PAattrib):
 n ZAtt,xx
 ;pragma fos RDlst
 m4_getListNodeListAttributes(ZAtt,RDlst,PDnode)
 m4_writeMJSON(ZAtt,forcetype="O",key=PDnode)
 q

Hierbij kan je gebruik maken van volgende macro’s :

macro writeMJSONStart($stype="L", $file=""):
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  Start de output van een MJSON set. Vanaf dan wordt alle output in de set verwerkt, tot een macro writeMJSONEnd is uitgevoerd.
    $stype: Type van de set. "L"=List, alles andere = dict.
    $file: Optioneel. Naam van de file, waarin de MJSON set terechtkomt. Leeg=stdout
    $example: m4_writeMJSONStart()
    $example: m4_writeMJSONStart(stype="D",file="myfile.json")
    '''

    «d %WritBeg^stdjsdp($stype,$file)»
macro writeMJSONEnd:
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  beeindig de output van een MJSON set
    $example: m4_writeMJSONEnd()
    '''

    «d %WritEnd^stdjsdp»
macro writeMJSON($mumpsref, $anetsource=r4_m_anetsource, $asciitarget=1, $forcetype="", $file="", $error=UDioerr, $key=""):
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  schrijft een MJSON structuur neer in JSON formaat in een bestand
    $mumpsref: referentie M structuur. Bevat de naam van de om te zetten MJSON structuur.
               Zowel globals als local arrays zijn welkom.
    $anetsource: Optioneel. 1 = $mumpsref is in Anet encoding | 0 = $mumpsref is in UTF-8 encoding
    $asciitarget: Optioneel. 1 = json is in ASCII encoding | 0 = json is in UTF-8 encoding
    $forcetype: Optioneel. Deze parameter heeft enkel effect op het hoogste niveau.
                   "" : de JSON codering wordt bepaald door de waarde in $mumpsref
                   "L" : de JSON codering is een list, ongeacht de waarde van @$mumpsref
                   Anders, dan is de JSON codering een dict, ongeacht de waarde van @$mumpsref
    $file: bestandsnaam, Indien leeg, dan wordt er op stdout geschreven.
           bestaat het bestand reeds, dan wordt het overschreven.
           Het bestand wordt ook afgesloten
    $error: Optioneel. Naam van de variabele, die een foutboodschap bevat. 0: correct weggeschreven | fout melding
    $key: Optioneel. Laat de MJSON structuur voorafgaan aan "$key": in JSON formaat. Doet dit enkel als $key niet leeg is.
    $example: m4_writeMJSON(^BCAT("irua",10), file="/library/tmp/rphilips.json")
    $example: m4_writeMJSON(RA)
    $example: m4_writeMJSON(RA,key="c:lvd:6789")
    '''

    «s MDr=$na($mumpsref) s $error=$$%Write^stdjsdp(MDr,$anetsource,$asciitarget,$forcetype,$file,$key)»

Notitie

Bemerk twee dingen :

  • Macro writeMJSON past zich onderhuids aan, als ze wordt opgeroepen tussen een m4_writeMJSONStart en een m4_writeMJSONEnd. Je hoeft dus geen rekening te houden met extra , notaties, en dergelijke.
  • In ons voorbeeld wordt een extra parameter key=PDnode doorgegeven.
Hoe roep je dit op in de Python omgeving ?
Maak gebruik van spawner.iterMJSON : in ons voorbeeld wordt dit:
>>> from anet.core import spawner
>>> lstloi = "lst:6635861"
>>> miter = [
        ('set', 'RDlst', lstloi),
        ('exec', 'd %Lst^stdemojs(RDlst)')
        ]
>>> reccnt = 0
>>> for record in spawner.iterMJSON(iterable=miter):
        reccnt += 1
        print("record %s" % reccnt)
        print(record)

>>> record 1
(u'o:lvd:99894', {u'sigillum': u'EHC-MSX'})
>>> record 2
(u'o:lvd:99950', {u'sigillum': u'EHC-MSX'})

21.7. Het ontvangen van MJSON sets in M

Maak gebruik van volgende macro:

macro looploadMJSON($onexe, $anetsource=r4_m_anetsource, $anettarget=r4_m_anetsource, $readexe=""):
    '''
    $editfile: /stdlib/json/json.d
    $synopsis:  lees in een loop een MJSON string set in, zet om naar een M structur, en roep voor elk gevonden object $onexe op
    $onexe: M executable, die moet worden uitgevoerd. Deze baseert zich op M array RAm (die de M structuur bevat), en RDnode (die de 'key' waarde bevat, of een teller, ingeval de set een list is)
    $anetsource: Optioneel. 1 = json is in Anet encoding | 0 = json is in UTF-8 encoding
    $anettarget: Optioneel. 1 = mumps is in Anet encoding | 0 = mumps is in UTF-8 encoding
    $readexe: Optioneel. Welke leesinstructie moet worden gebruikt. De inhoud van de gelezen JSON string moet zich bevinden in RDread. RDread=leeg bij EOFile. Default = lees JSON van de current device.
    $example: m4_looploadMJSON("One^myrou(.RAm,RDnode)")
    '''

    «d %RLoad^stdjsld($onexe,$readexe,$anetsource,$anettarget)»