Hallo Ihr beiden,
Vielen Dank, aber ich habe ja auch einen speziellen Fall. Ich weiss genau auf welche Felder ich zugreifen möchte. Weiterhin ist sicher, dass das PIPE Zeichen (|) nicht vorkommt. Deshalb konnte ich folgenden Code schreiben. Das ist kein vollständiger
JSON Parser, und ich hab den nur eben in zwei Stunden hingesudelt. In meinem Fall klappt es aber ausreichend gut:
Code: Alles auswählen
; JSON decoder
; (w) & (c) 2009 by Volker Schmid
; PureBasic 4.2
;
; Utilizes Mapping-Code by NicTheQuick (July 2003)
; ##########################
; ### MAPPING CODE ###
; ##########################
Structure jsonMap
Index.s
Value.s
EndStructure
Global NewList jsonMap.jsonMap()
Procedure jsonMapPut(Index.s, Value.s)
Protected *z1.Byte, *z2.Byte, OK.l
ResetList(jsonMap())
While NextElement(jsonMap())
*z1 = @Index
*z2 = @jsonMap()\Index
While *z1\b = *z2\b And *z1\b And *z2\b
*z1 + 1
*z2 + 1
Wend
If *z1\b = *z2\b
jsonMap()\Value = Value
ProcedureReturn @jsonMap()
EndIf
Wend
If AddElement(jsonMap())
jsonMap()\Index = Index
jsonMap()\Value = Value
ProcedureReturn @jsonMap
EndIf
ProcedureReturn #False
EndProcedure
Procedure.s jsonMapGet(Index.s)
Protected *z1.Byte, *z2.Byte, OK.l
ResetList(jsonMap())
While NextElement(jsonMap())
*z1 = @Index
*z2 = @jsonMap()\Index
While *z1\b = *z2\b And *z1\b And *z2\b
*z1 + 1
*z2 + 1
Wend
If *z1\b = *z2\b
ProcedureReturn jsonMap()\Value
EndIf
Wend
ProcedureReturn ""
EndProcedure
; ##########################
; ### JSON DECODE ###
; ##########################
Procedure.l jsonHex2Dec(Input.s)
; "FF" = 256 "65A" = 1626
Protected i.l, d.l
For i.l = 1 To Len(Input.s)
d.l = (d.l << 4) + Asc(UCase(Mid(Input.s, i.l, 1))) - 48 - 7 * (Asc(UCase(Mid(Input.s, i.l, 1))) >> 6)
Next
ProcedureReturn d.l
EndProcedure
Procedure.b jsonDecode(JSONString.s)
ClearList(jsonMap()) ; init list
JSONString.s = Trim(JSONString.s)
If Left(JSONString.s, 1) <> "{": ProcedureReturn #False: EndIf
Parent.s = ""
For x.l = 2 To Len(JSONString.s)
Char.s = Mid(JSONString.s, x.l, 1)
Select Char.s
Case Chr(34)
If InString.b = #False: InString.b = #True: Else: InString.b = #False: EndIf
Case ","
If InString.b = #False And Word.s <> ""
If InArray.b = #True: ArrayCount.l = ArrayCount.l + 1: EndIf
LastValue.s = Word.s
Word.s = ""
If InArray.b = #False
;Debug "-> " + Parent.s + LastParameter.s + ": " + LastValue.s
jsonMapPut(Parent.s + LastParameter.s, LastValue.s)
Else
;Debug "-> " + Parent.s + LastParameter.s + "|" + Str(ArrayCount) + ": " + LastValue.s
jsonMapPut(Parent.s + LastParameter.s + "|" + Str(ArrayCount.l), LastValue.s)
EndIf
EndIf
Case ":"
If InString.b = #False
LastParameter.s = Word.s
LastValue.s = ""
Word.s = ""
Else
Word.s = Word.s + Char.s
EndIf
Case "{"
If InString.b = #False
Parent.s = Parent.s + LastParameter.s + "|"
Word.s = ""
Else
Word.s = Word.s + Char.s
EndIf
Case "}"
If InString.b = #False
LastValue.s = Word.s
Word.s = ""
If InArray.b = #False
If LastValue.s <> ""
; Debug "ia -> " + Parent.s + LastParameter.s + ": " + LastValue.s
jsonMapPut(Parent.s + LastParameter.s, LastValue.s)
EndIf
Else
; Debug "na -> " + Parent.s + LastParameter.s + "|" + Str(ArrayCount.l) + ": " + LastValue.s
jsonMapPut(Parent.s + LastParameter.s + "|" + Str(ArrayCount.l), LastValue.s)
EndIf
NewParent.s = ""
For y.l = 1 To CountString(Parent.s , "|")-1
NewParent.s = StringField(Parent.s, y.l, "|") + "|"
Next
Parent.s = NewParent.s
Else
Word.s = Word.s + Char.s
EndIf
Case "["
If InString.b = #False
; Parent.s = Parent.s + LastParameter.s + "|"
Word.s = ""
InArray.b = #True
ArrayCount.l = 0
Else
Word.s = Word.s + Char.s
EndIf
Case "]"
If InString.b = #False
LastValue.s = Word.s
Word.s = ""
If InArray.b = #True: ArrayCount.l = ArrayCount.l + 1: EndIf
If InArray.b = #False
;Debug "-> " + Parent.s + LastParameter.s + ": " + LastValue.s
jsonMapPut(Parent.s + LastParameter.s, LastValue.s)
Else
;Debug "-> " + Parent.s + LastParameter.s + "|" + Str(ArrayCount.l) + ": " + LastValue.s
jsonMapPut(Parent.s + LastParameter.s + "|" + Str(ArrayCount.l), LastValue.s)
EndIf
Else
Word.s = Word.s + Char.s
EndIf
InArray.b = #False
Case "\"
x.l = x.l + 1
Char.s = Mid(JSONString.s, x.l, 1)
Select Char.s
Case Chr(34): Word.s = Word.s + Chr(34)
Case "\": Word.s = Word.s + "\" ; backslash
Case "/": Word.s = Word.s + "/" ; slash
Case "b": Word.s = Word.s + Chr(8) ; backspace
Case "f": Word.s = Word.s + Chr(14); formfeed
Case "n": Word.s = Word.s + #LF$ ; new line
Case "r": Word.s = Word.s + #CR$ ; carriage return
Case "t": Word.s = Word.s + Chr(9) ; tabulator
Case "u" ; hex 4 digits
x.l = x.l + 1
Char1.s = Mid(JSONString.s, x.l, 2)
x.l = x.l + 2
Char2.s = Mid(JSONString.s, x.l, 2)
x.l = x.l + 2
Word.s = Word.s + Chr(jsonHex2Dec(Char1.s)) + Chr(jsonHex2Dec(Char2.s))
EndSelect
Case " "
If InString.b = #True
Word.s = Word.s + Char.s
EndIf
Default
Word.s = Word.s + Char.s
EndSelect
Next
EndProcedure
; ######################
; ### TEST CODE ###
; ######################
JSON.s = "{" + Chr(34) + "Kreditkarte" + Chr(34) + " : " + Chr(34) + "Xema" + Chr(34) + "," + Chr(34) + "Nummer" + Chr(34) + " : " + Chr(34) + "1234-5678-9012-3456" + Chr(34) + "," + Chr(34) + "Inhaber" + Chr(34) + " : {" + Chr(34) + "Name" + Chr(34) + " : " + Chr(34) + "Reich" + Chr(34) + "," + Chr(34) + "Vorname" + Chr(34) + " : " + Chr(34) + "Rainer" + Chr(34) + "," + Chr(34) + "Geschlecht" + Chr(34) + " : " + Chr(34) + "männlich" + Chr(34) + "," + Chr(34) + "Vorlieben" + Chr(34) + " : [ " + Chr(34) + "Reiten" + Chr(34) + ", " + Chr(34) + "Schwimmen" + Chr(34) + ", " + Chr(34) + "Lesen" + Chr(34) + " ]," + Chr(34) + "Alter" + Chr(34) + " : null}," + Chr(34) + "Deckung" + Chr(34) + " : 2e+6," + Chr(34) + "Währung" + Chr(34) + " : " + Chr(34) + "EURO" + Chr(34) + "}"
jsonDecode(JSON.s)
Debug "Direct Access:"
Debug "Nummer: " + jsonMapGet("Nummer")
Debug "Vorname: " + jsonMapGet("Inhaber|Vorname")
Debug "Vorliebe 2" + jsonMapGet("Inhaber|Vorlieben|2")
Debug " "
Debug "Array content:"
ForEach jsonMap()
Debug jsonMap()\Index + ": " + jsonMap()\Value
Next
Man kann einfach über einen String auf die gewünschten Objekte und Daten zugreifen. Arrays werden mit 1 beginnend durchnumeriert.
Was dieser Code (noch) nicht kann:
- kein True, False (wird als String geliefert)
- kein "null" (wird als String geliefert)
- Keine Befehle um Arrays durchzulaufen oder deren Anzahl zu ermitteln etc.
In meinem Fall ist das absolut ausreichend. Wer Lust verspürt, kann das ja entsprechend erweitern und hier posten.
[EDIT 12.10.2009] verbessertes Handling von Arrays. Rückgabe von Strings in Anführungszeichen zur Unterscheidung (kann man ja dann rausfiltern).
[EDIT 04.02.2011] unterstützt jetzt auch die Zeichen : [ ] { } in Namen und Werten. Kleinen Bug rausgenommen.
Grüsse,
Volker