json Daten - Struktur - Map-Zuordnung moeglich?

Anfängerfragen zum Programmieren mit PureBasic.
oO0XX0Oo
Beiträge: 55
Registriert: 21.07.2017 22:36

json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von oO0XX0Oo »

Hallo,

Ich lese zur Zeit Daten aus einer .json Datei aus und extrahiere sie dann in eine Variable, die einer Struktur zugeordnet ist.

Code: Alles auswählen

Structure JSON_ITEM
  Name.s
  Row.i
  Type.i
  CompareWith.s
EndStructure

Structure JSON_SHEET
  List Items.JSON_ITEM()
  Name.s
  FirstClientRow.i
  FirstClientColumn.i
EndStructure

Structure JSON_FORM
  Revision.s
  LastSaved.i
  RequiredSheets.s
  List Sheets.JSON_SHEET()
EndStructure

Global Form.JSON_FORM

Define.i hJSON
Define.s jsonFile
jsonFile = "R:\_json.json"

hJSON = LoadJSON(#PB_Any, jsonFile)
If hJSON
  ExtractJSONStructure(JSONValue(hJSON), @Form.JSON_FORM, JSON_FORM)
Else
  Debug "Malformed JSON detected!" + #CRLF$ + #CRLF$ +
        "Message: " + JSONErrorMessage() + #CRLF$ +
        "Line:    " + JSONErrorLine() + #CRLF$ +
        "Column:  " + JSONErrorPosition()
EndIf
Das ist soweit fein und unproblematisch. Das Ganze wird aber spaeter im Programm sehr zeitkritisch,
weil ich in einem aeusseren foreach loop jedesmal durch die Liste Items.JSON_ITEM() durchgehen
muss, um den Eintrag "Name" von dort mit einem anderen String zu vergleichen.

Viel schneller ginge es, wenn ich es mit einem FindMapElement() machen koennte, was mir gleichzeitig
auch sofort den Zeiger auf das Element liefern wuerde, dessen Daten ich brauche...

Deshalb die Frage ob es moeglich ist, die folgenden .json Daten so zu veraendern, dass ich statt
List Items.JSON_ITEM()
Map Items.JSON_ITEM()
in der JSON_SHEET Struktur verwenden kann.

Also das "S1_PV_K_GM", "S1_PV_K_GW", etc. zu "MapKeys" werden?

Anmerkung:
UTF-8 Datei erstellen, folgende Daten reinkopieren, als "_json.json" abspeichern und Pfad anpassen:

Code: Alles auswählen

   {
      "Revision"       : "v1",
      "LastSaved"      : 23432432,
      "RequiredSheets" : "GS,MB",
      "Sheets"         : [
         {
            "Name"              : "GS",
            "FirstClientColumn" : 6,
            "FirstClientRow"    : 7,
            "Items"             : [
               {
                  "Name"        : "S1_PV_K_GM",
                  "Row"         : 7,
                  "Type"        : 1,
                  "CompareWith" : "männlich"
               },
               {
                  "Name"        : "S1_PV_K_GW",
                  "Row"         : 7,
                  "Type"        : 1,
                  "CompareWith" : "weiblich"
               },
               {
                  "Name"        : "S1_PV_K_FN",
                  "Row"         : 8,
                  "Type"        : 2,
                  "CompareWith" : ""
               }
            ]
         },
         {
            "Name"              : "MB",
            "FirstClientColumn" : 6,
            "FirstClientRow"    : 5,
            "Items"            : [
               {
                  "Name"        : "S4_ERG_IB_Y",
                  "Row"         : 203,
                  "Type"        : 1,
                  "CompareWith" : "ja"
               }
            ]
         }
      ]
   }
Benutzeravatar
Bisonte
Beiträge: 2427
Registriert: 01.04.2007 20:18

Re: json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von Bisonte »

Wenn ich das richtig verstehe ist die Json-Datei nicht von dir ?

Dann ist es recht simple, wenn du in die Struktur selbst eine
MAP einfügst, und den dann mit den Adressen der Elemente füllst. Am besten gleich nach ExtractJSONStructure.
Dieses wird bei ExtractJSONStructure übersprungen, weil kein Element so heisst... also bleibt es leer.

Code: Alles auswählen

Structure JSON_ITEM
  Name.s
  Row.i
  Type.i
  CompareWith.s
EndStructure

Structure JSON_SHEET
  List Items.JSON_ITEM()
  Map *ItemName()
  Name.s
  FirstClientRow.i
  FirstClientColumn.i
EndStructure

Structure JSON_FORM
  Revision.s
  LastSaved.i
  RequiredSheets.s
  Map *SheetName()
  List Sheets.JSON_SHEET()
EndStructure

Global Form.JSON_FORM

Define.i hJSON
Define.s jsonFile
jsonFile = "d:\_json.json"

hJSON = LoadJSON(#PB_Any, jsonFile)
If hJSON
  ExtractJSONStructure(JSONValue(hJSON), @Form.JSON_FORM, JSON_FORM)
  ForEach Form\Sheets()
    Form\SheetName(Form\Sheets()\Name) = @Form\Sheets()
    ForEach Form\Sheets()\Items()
      Form\Sheets()\ItemName(Form\Sheets()\Items()\Name) = @Form\Sheets()\Items()
    Next
  Next
  
Else
  Debug "Malformed JSON detected!" + #CRLF$ + #CRLF$ +
        "Message: " + JSONErrorMessage() + #CRLF$ +
        "Line:    " + JSONErrorLine() + #CRLF$ +
        "Column:  " + JSONErrorPosition()
EndIf

Define *Sheet.JSON_SHEET
Define *Item.JSON_ITEM

If FindMapElement(Form\SheetName(), "GS")
  *Sheet = Form\SheetName()
  If FindMapElement(*Sheet\ItemName(), "S1_PV_K_FN")
    *Item = *Sheet\ItemName()
  EndIf
EndIf

If *Item
  Debug *Item\Name
  Debug *Item\Row
  Debug *Item\Type
  Debug *Item\CompareWith
  *Item = #Null
EndIf
PureBasic 6.04 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
oO0XX0Oo
Beiträge: 55
Registriert: 21.07.2017 22:36

Re: json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von oO0XX0Oo »

Hallo Bisonte,

vielen Dank fuer deine Hilfe!

Mit meiner alten Variante braucht das, was ich gerade tue bei einem Durchlauf
~440 ms (und ich brauche etwa 100 Durchlaeufe), also etwa 44 Sekunden.

Mit deiner Variante nur noch ~330 ms, respektive 33 Sekunden. :allright:
Wenn ich das richtig verstehe ist die Json-Datei nicht von dir ?
Doch, ist sie. Ich kann frei ueber ihren Aufbau bestimmen. Aendert das etwas
an deiner vorgeschlagenen Loesung?
Benutzeravatar
Bisonte
Beiträge: 2427
Registriert: 01.04.2007 20:18

Re: json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von Bisonte »

Dann kann man die Listen gleich in Maps umbauen.
PureBasic 6.04 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
oO0XX0Oo
Beiträge: 55
Registriert: 21.07.2017 22:36

Re: json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von oO0XX0Oo »

Ja, das war mein Anfangsgedanke. Dummerweise hab ich eine Denkblokade, wie ich das anstellen soll.
Irgendwie muss ich dann ja aus jedem "Name" unter "Items" direkt einen MapKey bauen und mir ist im
Moment unklar, wie das funktionieren soll...
Benutzeravatar
TroaX
Beiträge: 659
Registriert: 08.03.2013 14:27
Computerausstattung: PC: Ryzen 9 3950X, 96 GB RAM, RX6800XT, 2.5 TB SSD, 21:9 Display, Pop_OS! | Lappi: Ryzen 7 5800H, 16 GB RAM, 1 TB SSD, Pop_OS!
Wohnort: NRW
Kontaktdaten:

Re: json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von TroaX »

Bei solchen "komplexeren" JSON-Strukturen würde ich, wenn ich gezielt in den Daten suchen will, die Daten in eine "In-Memory-SQLite" überspielen. In der kann ich gezielt und schnell nach einem bestimmten Datensatz suchen.

JSON ist eigentlich nur dazu da, Objekte und damit verbunden Daten strukturiert als String über das HTTP-Protokoll zu übertragen. Um aber gezielt in den Daten zu suchen ist es ratsamer, die Daten in einen anderen Typ zu übertragen, der sich leichter durchsuchen lässt. SQLite eignet sich dafür 1000x besser als verschachtelte Strukturen, Listen und/oder Maps.

Gerade bei JSON-Files, die Datensätze enthalten und nicht nur ein Datensatz sind, würde ich immer diesen Umweg gehen.

Alternativ kannst du aber auch Datensätze per regulärem Ausdruck im JSON-File suchen und dann nur diesen in eine Struktur zu überführen. Aber dafür muss man richtig Plan über reguläre Ausdrucke haben.
PC: Ryzen 9 3950X | 96 GB RAM | RX6800XT | 2,5 TB NVMe | Pop_OS!
Notebook: 16" 3:2 | Ryzen 7 5800H | 16 GB RAM | Radeon Vega | 1TB NVMe | Pop_OS!
NAS: Fritz.Box :lol:
Coding: Purebasic 6.04 | PHP | HTML | CSS | Javascript
Benutzeravatar
Bisonte
Beiträge: 2427
Registriert: 01.04.2007 20:18

Re: json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von Bisonte »

In der Tat. Wenn viele Daten vorhanden sind und es auf Geschwindigkeit ankommt, dann SQLite.

Bei deiner Messung der Zeit für 100 Durchläufe, wäre es interessant zu sehen, wie das passiert...
und das der Debugger, bei solch einer Messung, nicht eingeschaltet sein darf, sollte auch klar sein ;)

Nichtsdestotrotz wäre das evt. so in der JSON Variante :

Code: Alles auswählen

Structure JSON_ITEM
  Name.s
  Row.i
  Type.i
  CompareWith.s
EndStructure
Structure JSON_SHEET
  Map Items.JSON_ITEM()
  Name.s
  FirstClientRow.i
  FirstClientColumn.i
EndStructure
Structure JSON_FORM
  Revision.s
  LastSaved.i
  RequiredSheets.s
  Map Sheets.JSON_SHEET()
EndStructure

Global Form.JSON_FORM

With Form
  \Revision = "v1"
  \LastSaved = 23432432
  \RequiredSheets = "GS,MB"  
EndWith

Procedure.i AddSheet(Name$, FirstClientRow, FirstClientColumn)  ; Keine Rückgabe
  
  Form\Sheets(Name$)\Name               = Name$
  Form\Sheets(Name$)\FirstClientRow     = FirstClientRow
  Form\Sheets(Name$)\FirstClientColumn  = FirstClientColumn
  
EndProcedure
Procedure.i AddItem(SheetName$, Name$, Row, Type, CompareWith$) ; Gibt #True zurück, wenn Item erstellt wurde, ansonsten #False
  
  Protected Result = #False
  
  If FindMapElement(Form\Sheets(), SheetName$)
    With Form\Sheets(SheetName$)\Items(Name$)
      \Name         = Name$
      \Row          = Row
      \Type         = Type
      \CompareWith  = CompareWith$
    EndWith
    Result = #True
  EndIf
  
  ProcedureReturn Result
  
EndProcedure
Procedure.i SaveData(File$)                                     ; Gibt #True zurück, wenn erfolgreich gespeichert, ansonsten #False
  
  
  Protected Result = #False, jSon = CreateJSON(#PB_Any, #PB_JSON_NoCase)
  
  If jSon
    InsertJSONStructure(JSONValue(jSon), @Form, JSON_FORM)
    Result = SaveJSON(jSon, File$, #PB_JSON_PrettyPrint)
    FreeJSON(jSon)
  EndIf
  
  ProcedureReturn Result
  
EndProcedure
Procedure.i LoadData(File$)                                     ; Gibt #True zurück, wenn geladen, ansonsten #False
  
  Protected Result = #False, jSon = LoadJSON(#PB_Any, File$, #PB_JSON_NoCase)
  
  If jSon
    ExtractJSONStructure(JSONValue(jSon), @Form, JSON_FORM)
    FreeJSON(jSon)
    Result = #True
  EndIf
  
  ProcedureReturn Result
  
EndProcedure

Procedure.i GetItem(SheetName$, ItemName$, *Item.JSON_Item)     ; Füllt *Item.JSON_ITEM. Wenn Erfolg dann #True, ansonsten #False
  
  Protected Result = #False
  
  If *Item
    If FindMapElement(Form\Sheets(), SheetName$)
      If FindMapElement(Form\Sheets()\Items(), ItemName$)
        *Item\Name        = Form\Sheets()\Items()\Name
        *Item\Row         = Form\Sheets()\Items()\Row
        *Item\Type        = Form\Sheets()\Items()\Type
        *Item\CompareWith = Form\Sheets()\Items()\CompareWith
        Result = #True
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn Result
  
EndProcedure

AddSheet("MB", 6, 5)
AddItem("MB", "S4_ERG_IB_Y", 203, 1, "ja")

AddSheet("GS", 6, 7)
AddItem("GS", "S1_PV_K_GM", 7, 1, "männlich")
AddItem("GS", "S1_PV_K_GW", 7, 1, "weiblich")
AddItem("GS", "S1_PV_K_FN", 8, 2, "ja")

; SaveData("d:\__jsonNEU.json")
; LoadData("d:\__jsonNEU.json")

Define Item.JSON_ITEM

If GetItem("GS", "S1_PV_K_GW", @Item)
  Debug Item\Name
  Debug Item\Row
  Debug Item\Type
  Debug Item\CompareWith
EndIf
PureBasic 6.04 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
oO0XX0Oo
Beiträge: 55
Registriert: 21.07.2017 22:36

Re: json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von oO0XX0Oo »

Danke Troax und Bisonte!

Pro json Datei hab ich im Maximum nicht mehr als 500 items und das ist schon relativ viel. Der Durchschnitt
bewegt sich eher bei 100-200. Ich bin nicht sicher, ob sich SQLite da schon wirklich lohnen wuerde aber
ich werde das mal implementieren und mir dann mal die Zeiten anschauen.
Bei deiner Messung der Zeit für 100 Durchläufe, wäre es interessant zu sehen, wie das passiert...
und das der Debugger, bei solch einer Messung, nicht eingeschaltet sein darf, sollte auch klar sein
Ich iteriere ueber ein COMateObject (COMatePLUS), genauer gesagt ueber eine Enumeration (Form fields
fuer ein Word Formular). Die noetigen Zuordnungen dafuer, was dann spaeter eingetragen werden soll (die
eigentlichen Elemente werden aus einer Excel-Tabelle geholt) stehen also in den .json Daten. Das Ganze
wird deshalb auf diese Art und Weise gemacht, damit ich fuer weitere Formulare das Hauptprogramm gar
nicht mehr anfassen brauch, sondern nur noch ein Formular mit Feldern erstelle und die zuegehoerige
.json Datei. Ich gebe fuer Geschwindigkeitstest die Angaben alle per OutputDebugString_ aus (und DbgView
stellt sie dann dar). Das Ganze nachdem die .pb Datei kompiliert wurde...
Benutzeravatar
TroaX
Beiträge: 659
Registriert: 08.03.2013 14:27
Computerausstattung: PC: Ryzen 9 3950X, 96 GB RAM, RX6800XT, 2.5 TB SSD, 21:9 Display, Pop_OS! | Lappi: Ryzen 7 5800H, 16 GB RAM, 1 TB SSD, Pop_OS!
Wohnort: NRW
Kontaktdaten:

Re: json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von TroaX »

Öhm wenn du per ComMate mit Excel und Word bei jedem Durchlauf interaggierst, dann wundern mich die Zeite nicht wirklich. So ganz raff ich immernoch nicht, was du da genau zusammenbaust. Was ist genau der Ausgangspunkt? Wo willst du hin? Wie sieht genau dein bisheriger Weg aus?

Zur Zeit sieht es nach "Daten kommen aus Excel, werden irgendwie zu JSON, müssen irgendwie in Word und wird per ComMate gemacht." aus. Dazwischen sind aber aktuell zu viele Fragezeichen.

Wenn du nur Daten aus Excel in Word-Dokumente überführen willst, dann kannst du das in der Regel mit den beiden Programmen direkt. Egal ob über Datenquellen oder VBA. Ich weiß nicht, was man gerade als Rat mitgeben kann. Ich würde den Flaschenhals gerade bei ComMate vermuten.
PC: Ryzen 9 3950X | 96 GB RAM | RX6800XT | 2,5 TB NVMe | Pop_OS!
Notebook: 16" 3:2 | Ryzen 7 5800H | 16 GB RAM | Radeon Vega | 1TB NVMe | Pop_OS!
NAS: Fritz.Box :lol:
Coding: Purebasic 6.04 | PHP | HTML | CSS | Javascript
oO0XX0Oo
Beiträge: 55
Registriert: 21.07.2017 22:36

Re: json Daten - Struktur - Map-Zuordnung moeglich?

Beitrag von oO0XX0Oo »

Ausgangspunkt ist der, dass Word-Formulare ausgefuellt werden sollen, deren Daten (zum Fuellen) aus Excel-Tabellen stammen u nd das gewuenschte Endprodukt sind wiederum .pdf Dateien (die aus Office 2010+ direkt gespeichert werden koennen).

Ja sicher, es gibt andere Moeglichkeiten, das zu tun, als per PureBasic / COMate. Serienbrief faellt aber aus, da die Excel-Tabellen vom Aufbau her fix sind und die eigentlichen Daten weder Spalten"-koepfe" besitzen, die als Ueberschriften herhalten koennen, noch die zu vearbeitenden Datei fortlaufend eingetragen sind. Das kann ich auch nicht aendern (Umformatierung ist nicht erwuenscht). VBA direkt unter Excel faellt aus, Makros sind in der Unternehmensinfrastruktur nicht erlaubt.

Man moechte unter einer einfachen Oberflaeche die Moeglichkeit eine Excel-Tabelle zu laden und zugehoerige Formulare (die Namen der zugehoerigen .docx Dateien entsprechen denen der Excel-Arbeitsblaetter) darueber ausgeben zu koennen.

Die Zuordnung per .json erlaubt es die Struktur der jeweiligen Excel-Arbeitsblaetter wiederzuspiegeln ohne das man fuer jedes
neue Formular das Hauptprogramm neu anfassen muesste.

Die Schritte die fuer das Ganze unternommen werden?
- Excel-Datei wird geladen
- Arbeitsblattnamen werden ausgelesen
- Zugehoerige .json Dateien werden geladen
- Drop-Down Felder zeigen an, fuer was Formulare erzeugt werden koennen
- Aufbauend darauf wird ein Formular geladen, ueber die Felder iteriert und
die noetigen Daten aus dem Excel-Arbeitsblatt geholt und eingetragen
- Abschliessend als .pdf gespeichert, Formular geloescht, rinse & repeat

Ja, COMate mag ein Flaschenhals sein, aber die Zeit um ein Word Formular mit 200 Elementen zu fuellen dauert im Moment weniger als 500ms und auch mit dem Loeschen aller Eintraege hinterher (das ist weniger zeitintensiv als das Formular ohne zu speichern zu schliessen und neu zu oeffnen) sind es keine 700 ms. Das ist von der Bearbeitungszeit vollkommen ok.

Ich haette nichts gegen eine schnellere Variante einzuwenden, ich kenne allerdings im Moment keine...
Antworten