!

Dette materialet blir ikke lenger vedlikeholdt. Du vil finne oppdatert materiale på siden: http://borres.hiof.no/wep/

JSON
Database
XSLT
Børre Stenseth
JavaScript >JSON

JSON

Hva
JSON , Javascriptobjekter

JSON er, slik vi skal betrakte det, en måte å pakke data på slik at de blir enkle å bearbeide på klientsiden. Hvis vi har AJAX som referanseramme betyr det at vi skal bruke JSON som format når vi returnerer svar på en forespørsel, en XMLHttpRequest. Vineksemplene nedenfor illustrerer dette, med ulike datakilder på tjeneren

JSON

Grunnlaget for JSON er at vi kan tolke tekstformat som Javascriptobjekter i et javaskript. Vi kan f.eks. gjøre slik:

var T='{"navn":"Ole","adresse":"Halden"}'
var person=eval("("+T+")");
alert(person.navn);	

Nøkkelen er eval-funksjonen som tolker teksten som evalueres som Javascriptkode. Siden Javascrupt er et interpretert språk er dette en vanlig og lett implementerbar mekanisme.

Vi kan også gjøre slik:

var T='{"personer":[{"navn":"Ole","adresse":"Halden"},{"navn":"Jens","adresse":"Moss"}]}'
var p=eval("("+T+")");
// var p=JSON.parse(T);
var person= p.personer;
alert(person[1].navn);	

Full forklaring av syntaksen finnes på [1] . Vi kan nøye oss med å konstatere at vi kan pakke attributter i objekter ved å angi "attributtnavn":"attributtverdi". Vi kan lage en kommaseparert liste av slike par. Videre ser vi at vi kan lage arrays med [].

Vi skal kunne forvente at browsere supporter et JSON-objekt. Dette skal ha to metoder:

  • parse() - som gjør det samme som den gamle metode eval
  • stringify() - som lage json av et objekt

Anta følgende Javascript:

var DATA='{"personer":[{"navn":"Ole","adresse":"Halden"},{"navn":"Jens","adresse":"Moss"}]}';
var obj=null;
var personer=null;

function extractNames(){
    if(obj==null){
        obj=JSON.parse(DATA);
        personer=obj.personer;
    }
    var s='';
    for(ix=0;ix<personer.length;ix++){
        s+=personer[ix].navn+'\n';
    }
    alert(s);
}
function changeandmakeJson(){
    if(obj==null){
        obj=JSON.parse(DATA);
        personer=obj.personer;
    }
    personer[0].navn="kristoffer";
    newObj={}
    newObj.personer=personer;
    S=JSON.stringify(newObj);
    alert(S);
}

test funksjonene her:

  • extractNames
  • changeandmakeJson

AJAX

Det interessante med denne angrepsvinkelen er altså at vi får svært enkel koding på klientsiden. Vi får tilgang til objekter og kan plukke attributter etter ønske.

Hvis vi ser dette i en AJAX situasjon, har vi følgende:

json-1
Produksjon av JSON

Hvorvidt bruk av JSON er en effektiv og/eller enkel metode avhenger av hvordan dataene våre er representerte på tjeneren, og det avhenger av hva vi må gjøre med dem på klienten. Det er klart at dersom vi skal overføre en hel fil som er ferdiglagd som et HTML-fragment og vi skal plassere dette fragmentet samlet på klienten er det mest effektive å bruke ren tekst slik:

	document.getElementById("target").innerHTML= myRequest.responseText

Dersom teksten som kommer tilbake består av mange "deler" som skal plasseres på forskjellige steder på siden stiller saken seg litt annerledes. Både kommaseparert tekst, XML og JSON kan være alternativer.

På tjenersiden vil mye avhenge av dataformatet og programmeringsverktøyet vi har til rådighet. Som regel kan vi vel regne med at programmeringsverktøyet på tjeneren er bedre og raskere enn Javascript. Dette skulle i så fall tale for at så mye som mulig gjøres klart på tjeneren.

På den annen side er det ofte slik at bruken av de data vi sender tilbake vil endre seg i forskjellige situasjoner avhengig av bruksønsker og designbeslutninger. Det er kanskje en fordel å lage en generell tjenerfunksjon og så lage spesielle klientløsninger etter ønske. Dette taler for et fleksibelt format som peker i retning av JSON, eller XML. De to, JSON og XML, kan langt på vei sidestilles prinsippielt, men JSON gjør trolig lettere å programmere på klienten.

Nedenfor skal vi gå gjennom noen eksempler på pakking av data på tjenersiden og bruk på klientsiden. Vi skal bruke vindataene som gjennomgangseksempel. Disse er beskrevet i modulen Noen datasett . Vi skal bruke både XML-versjonen og databaseversjonen. Vi skal i alle tilfellene lage en klientside som gir oss mulighet for å velge vin av en bestemt type (rød, hvit, rose, musserende) og fra et bestemt land. Vi skal i alle løsningen bruke JSON, selv om et par av eksemplene åpenbart kunne løses med lettere med tekst og alle kunne løses med XML.

Vindata

I alle eksemplene bruker vi et JSON format som ser slik ut, vist med linjeskift:

	{"diagnose":"OK",
	 "overskrift":"Rødvin fra Island",
	"viner":[
	{"navn":"Masi","terning":"4","beskrivelse":"OK"},
	{"navn":"Perequita","terning":"5","beskrivelse":"God"},
	...
	]}

Altså en diagnose som sier om dataoppslaget har gått bra eller ikke, en passelig overskrift pluss en array med vinobjekter. Merk at vi pakker dataene uten linjeskift slik at de kan leses som en sammenhengende string i Javascript.

I alle eksemplene bruker vi samme javascript på klienten:

_script1.js

Den interessante koden biten er funksjonen processJSON:

function processJSON(S,targetId)
{
    var p = null;
    //alert(S);
    try{
        p = eval("(" + S + ")");
    }
    catch(ex)
    {
        document.getElementById('result').innerHTML=
            '<p>Feil i dataformatet: '+ex.message+'</p>';
        return;
    }
    var dia=p.diagnose;
    if(dia.indexOf('OK')==0)
    {
        var vinlist=p.viner;
        // manual count due to diff between browsers on empty fields
        var count=0;
        var S='';
        for(var ix=0;ix<vinlist.length;ix++)
        {
            try
            {
              S+='<p>';
              S+='<span style="font-size:20px;color:blue;margin-right:20px">'+
                   vinlist[ix].terning+'</span>';
              S+=vinlist[ix].navn+'<br/>';
              S+=vinlist[ix].beskrivelse+'<br/>';
              S+='</p>';
              count++;
            }
            catch(e)
            {
            }
        }
        var T='<h1>'+count+' '+p.overskrift+'</h1>';
        document.getElementById(targetId).innerHTML=T+S;
    }
    else
    {
        document.getElementById(targetId).innerHTML='<pre>'+dia+'</pre>';
    }
}

En typisk tekst som overføres og parses ser slik ut: typisk string

Vin fra XML

Vi bruker XML-data på tjeneren, skriver DOM-code som identifiserer de vinene vi er interesserte i og produserer JSON. På klientsiden lager vi HTML av JSON-formatet.

Du kan teste her http://www.it.hiof.no/~borres/dw/json/page1.html

Koden på tjeneren ser slik ut:

#! /usr/bin/python
# -*- coding: utf-8 -*-
import xml.dom.minidom,urllib,cgi
#-------------------------------------------------------------
# Receive: land and vintype
# Return JSON format for selected wines
#{"diagnose":"OK",
# "overskrift":"Hvite viner fra Italia",
# "viner":[
#         {"navn":"A","terning":"4","beskrivelse":"grei"},
#         {"navn":"B","terning":"4","beskrivelse":"ok"}
#         ]
#}
# B. Stenseth  2009
#-------------------------------------------------------------
#------------------------------------------------------
# datasource
XMLSOURCE='http://www.it.hiof.no/~borres/commondata/vin/viner.xml'
#------------------------------------------------------
# string for one wine
wine="""{"navn":"%s","terning":"%s","beskrivelse":"%s"}"""
#-----------------------
# downloading an xml-file as string
def loadUrl(urladdress):
    try:
        f=urllib.urlopen(urladdress)
        s=f.read()
        f.close()
        return s
    except:
        return None
#-----------------------        
# collect all text in a node
def getText(nodelist):
    rc = ''
    for node in nodelist:
        if node.nodeType == node.TEXT_NODE:
            #t=node.data.encode('ISO-8859-1')
            t=node.data.encode('utf-8')
            rc += t
    return rc
#--------------------------
# prepare one wine
def doOneWine(vin,theCountry,theType):
    land=getText(vin.getElementsByTagName('country')[0].childNodes)
    if land != theCountry:
        return ''
    vintype=getText(vin.getElementsByTagName('type')[0].childNodes)
    if vintype != theType:
        return ''
    navn=getText(vin.getElementsByTagName('name')[0].childNodes)
    navn=navn.replace('"','\\"')
    terning=getText(vin.getElementsByTagName('dice')[0].childNodes)
    beskrivelse=getText(vin.getElementsByTagName('description')[0].childNodes)
    return wine%(navn,str(terning),beskrivelse)

#--------------------------
# buld tree and viner string
def doAll(theCountry,theType):
    try:
        vintxt=loadUrl(XMLSOURCE)
        if vintxt==None:
            return None
        vindom = xml.dom.minidom.parseString(vintxt)
        vinlist=vindom.getElementsByTagName('wine')
        result=''
        for vin in vinlist:
            t=doOneWine(vin,theCountry,theType)
            if len(t)>0:
                result=result+t+','
        return result
    except:
        return None
#----------------------
form=cgi.FieldStorage()
print 'Content-type: text/html; charset=utf-8\n'
land='Ukjent'
vintype='Ukjent'
dia='"diagnose":"BAD"'
if form.has_key('land'):
    land=form['land'].value
if form.has_key('vintype'):
    vintype=form['vintype'].value
    
if vintype=='red':
    header='"overskrift":"Røde viner fra %s"'%land
elif vintype=='white':
    header='"overskrift":"Hvite viner fra %s"'%land
elif vintype=='rose':
    header='"overskrift":"Roseviner fra %s"'%land
elif vintype=='sparkling':
    header='"overskrift":"Musserende viner fra %s"'%land
else:
    header='"overskrift":"Ukjent vintype"'

viner=doAll(land,vintype)
if viner !=None:
    dia='"diagnose":"OK"'
else:
    viner='xx'
retstr='{'+dia+','+header+',"viner":['+viner[:-1]+']}'
print retstr

VIN fra XML via XSLT

Vi bruker XML-data på tjeneren, skriver en XSLT-transformasjon som lager JSON direkte. På klientsiden lager vi HTML av JSON-formatet.

Du kan teste her http://www.it.hiof.no/~borres/dw/json/page2.html

Koden på tjeneren ser slik ut:

#! /usr/bin/python
# -*- coding: utf-8 -*-
import xml.dom.minidom,urllib,cgi
from lxml import etree
import cgitb; cgitb.enable()

#-------------------------------------------------------------
# Receive: land and vintype
# Return JSON format for selected wines
#{"diagnose":"OK",
# "overskrift":"Hvite viner fra Italia",
# "viner":[
#         {"navn":"A","terning":"4","beskrivelse":"grei"},
#         {"navn":"B","terning":"4","beskrivelse":"ok"}
#         ]
#}
# B. Stenseth  2009
#-------------------------------------------------------------
#------------------------------------------------------
# data and transform source
XMLSOURCE='http://www.it.hiof.no/~borres/commondata/vin/viner.xml'
XSLSOURCE='http://www.it.hiof.no/~borres/cgi-bin/json/vin/trans2.xsl'
#------------------------------------------------------
# string for one wine
wine="""{"navn":"%s","terning":"%s","beskrivelse":"%s"}"""
#-----------------------
# helper to load a text file
def getTextFile(filename):
    try:
        file=open(filename,'r')
        intext=file.read()
        file.close()
        return intext
    except:
        return None
#-----------------------
# downloading an xml-file as string
def loadUrl(urladdress):
    try:
        f=urllib.urlopen(urladdress)
        s=f.read()
        f.close()
        return s
    except:
        return None
#-----------------------  
def doAll(land,vintype):
    xsltstr=loadUrl(XSLSOURCE)
    xmlstr=loadUrl(XMLSOURCE)
    xmlstr=xmlstr.strip()
    # hack to keep "
    parts=xmlstr.split('\n',1)
    xmlstr=parts[0]+'\n'+parts[1].replace('"','\\"')
    xmlTree=etree.XML(xmlstr)
    xsltTree=etree.XML(xsltstr)
    transform=etree.XSLT(xsltTree)
    c=etree.XSLT.strparam(land)
    t=etree.XSLT.strparam(vintype)
    resultTree=transform(xmlTree,theCountry=c,theType=t)
    return str(resultTree)

#----------------------
form=cgi.FieldStorage()
print 'Content-type: text/html; charset=utf-8\n'
land='Portugal'
vintype='red'
dia='"diagnose":"BAD"'
if form.has_key('land'):
    land=form['land'].value
if form.has_key('vintype'):
    vintype=form['vintype'].value
    
if vintype=='red':
    header='"overskrift":"Røde viner fra %s"'%land
elif vintype=='white':
    header='"overskrift":"Hvite viner fra %s"'%land
elif vintype=='rose':
    header='"overskrift":"Roseviner fra %s"'%land
elif vintype=='sparkling':
    header='"overskrift":"Musserende viner fra %s"'%land
else:
    header='"overskrift":"Ukjent vintype"'
    
viner=doAll(land,vintype)
if len(viner) > 0:
    dia='"diagnose":"OK"'
else:
    viner='xx'

retstr='{'+dia+','+header+','+viner+'}'
print retstr

Transformasjonen er slik:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
  <xsl:param name="theCountry" select="'Frankrike'"/>
  <xsl:param name="theType" select="'red'"/>
  <xsl:template match="/">
    "viner":[
    <xsl:apply-templates select="//wine[country=$theCountry and type=$theType]">
      <xsl:sort order="descending" select="dice"/>
    </xsl:apply-templates>
    ]
  </xsl:template>
  <xsl:template match="//wine">
    {"terning":"<xsl:value-of select="dice"/>",
     "navn":"<xsl:value-of select="name"/>",
    "beskrivelse":"<xsl:value-of select="description"/>"},
  </xsl:template>
</xsl:stylesheet> 

Vin fra database

Vi bruker en database på tjeneren og lager JSON basert på de recordene vi plukker opp med en SQL-setning. På klientsiden lager vi HTML av JSON-formatet.

Du kan teste her http://www.it.hiof.no/~borres/dw/json/page3.html

Koden på tjeneren ser slik ut:

#! /usr/bin/python2.5
# -*- coding: utf-8 -*-
import MySQLdb,cgi,urllib
#-------------------------------------------------------------
# Receive: land and vintype
# Return JSON format for selected wines
#{"diagnose":"OK",
# "overskrift":"Hvite viner fra Italia",
# "viner":[
#         {"navn":"A","terning":"4","beskrivelse":"grei"},
#         {"navn":"B","terning":"4","beskrivelse":"ok"}
#         ]
#}
# Datasource: mySQL base as described in
# http://www.ia.hiof.no/~borres/ml/datasett/p-datasett.html
# B. Stenseth  2007
#-------------------------------------------------------------
#-------------------------------------------------
# connect and execute a sql-request
#-------------------------------------------------
def connectAndExecute(sql):
    try:
        myBase=MySQLdb.connect(host='frigg.hiof.no',
                               user='student',
                               passwd='student',
                               db='vin')
        myTab=myBase.cursor()
        myTab.execute(sql)
        myBase.close()
        return myTab.fetchall()
    except MySQLdb.Error, e:
        print "Error %d: %s" % (e.args[0], e.args[1])
        return None
#-------------------------------------------------
# do the job
#-------------------------------------------------
def doAll(land,vintype):
    resultTxt=''
    sql="""select name,dice,description from wines
           where country='%s' and type='%s'
           order by dice desc;"""
    result=connectAndExecute(sql%(land,vintype))
    if result==None:
        return None
    wine="""{"navn":"%s","terning":"%s","beskrivelse":"%s"}"""
    for row in result:
        navn=str(row[0])
        navn=navn.replace('"','\\"')
        terning=str(row[1])
        beskrivelse=str(row[2])
        resultTxt=resultTxt+wine%(navn,terning,beskrivelse)+','
    return resultTxt
#----------------------
form=cgi.FieldStorage()
print 'Content-type: text/html; charset=utf-8\n'
land='Ukjent'
vintype='Ukjent'
dia='"diagnose":"BAD"'
if form.has_key('land'):
    land=form['land'].value
if form.has_key('vintype'):
    vintype=form['vintype'].value
    
if vintype=='red':
    header='"overskrift":"Røde viner fra %s"'%land
elif vintype=='white':
    header='"overskrift":"Hvite viner fra %s"'%land
elif vintype=='rose':
    header='"overskrift":"Roseviner fra %s"'%land
elif vintype=='sparkling':
    header='"overskrift":"Musserende viner fra %s"'%land
else:
    header='"overskrift":"Ukjent vintype"'
    
viner=doAll(land,vintype)
    
if viner != None:
    dia='"diagnose":"OK"'
    viner=viner.decode('iso-8859-1')
    viner=viner.encode('utf-8')
else:
    viner='xx'
retstr='{'+dia+','+header+',"viner":['+viner[:-1]+']}'
print retstr
Referanser
  1. JSON(JavaScript Object Notation) json.org www.json.org/ 14-03-2010
  1. JSONLint arc90 jsonlint.com/ 02-11-2012
  1. Json Parser Online json.parser.online.fr/ 02-11-2012
Vedlikehold
B.Stenseth, revidert oktober 2009
( Velkommen ) JavaScript >JSON ( Å lese forms )