0

(JSON) Object parser Browser vs. Client

Ich verzweifle an etwas, das ich als Bug bezeichnen würde.

Ich habe eine Funktion (global), die im Browser problemlos geht, aber in der App fehlschlägt und das gesamte globale Skript zerschießt.

Das Problem besteht darin, ein JSON-Objekt in einer Liste zu speichern, hier um ein gültiges GeoJSON zu erzeigen mit.

features: [{...}]

 

Wenn ich diese Liste nicht erzwinge (damit aber kein gültiges GeoJSON), ist es kein Problem.

Der Fehler deutet darauf hin, dass in ein Objekt geschrieben wird, das nicht existiert.

Hier zwei Versuche, beide nicht erfolgreich:

Version 1

function make_GeoJSON(ref : Places,JJ : text) do
    let fJ := [{}];
    let gJ := {};
    let pJ := {
            PLACE_ID: number(ref),
            PLACE_NAME: ref.Name,
            PLACE_TYPE: ref.Type.Name,
            PLACE_TYPE_ID: ref.Type.number(Id),
            distance: 0
        };
    if JJ = "" then JJ := ref.GeoJSON end;
    let J := parseJSON(JJ);
    if cnt(J.features) > 0 then
        fJ := [J.first(features)];
        if ref.'Primary Location' and longitude(ref.'Primary Location') != 0 and
                        latitude(ref.'Primary Location') != 0 and
                    longitude(ref.'Primary Location') and
                latitude(ref.'Primary Location') and
            first(fJ).geometry.type = "Point" then
            setItem(first(fJ).geometry, "coordinates", [longitude(ref.'Primary Location'), latitude(ref.'Primary Location')])
        end;
        if not first(fJ).properties then
            setItem(first(fJ), "properties", pJ)
        end;
        if not first(fJ).properties.distance then
            setItem(first(fJ).properties, "distance", 0)
        end;
        setItem(first(fJ), "id", number(ref));
        setItem(first(fJ).properties, "PLACE_ID", number(ref));
        setItem(first(fJ).properties, "PLACE_NAME", ref.Name);
        setItem(first(fJ).properties, "PLACE_TYPE", ref.Type.Name);
        setItem(first(fJ).properties, "PLACE_TYPE_ID", ref.Type.number(Id));
        setItem(J, "features", fJ)
    else
        void
    end;
    formatJSON(J)
end;

 

Hier ist das Problem in:

 let fJ := [{}];

 

Version 2

function make_GeoJSON(ref : Places,JJ : text) do
    let fJ := {};
    let gJ := {};
    let pJ := {
            PLACE_ID: number(ref),
            PLACE_NAME: ref.Name,
            PLACE_TYPE: ref.Type.Name,
            PLACE_TYPE_ID: ref.Type.number(Id),
            distance: 0
        };
    if JJ = "" then JJ := ref.GeoJSON end;
    let J := parseJSON(JJ);
    if cnt(J.features) > 0 then
        fJ := J.first(features);
        if ref.'Primary Location' and longitude(ref.'Primary Location') != 0 and
                        latitude(ref.'Primary Location') != 0 and
                    longitude(ref.'Primary Location') and
                latitude(ref.'Primary Location') and
            fJ.geometry.type = "Point" then
            setItem(fJ.geometry, "coordinates", [longitude(ref.'Primary Location'), latitude(ref.'Primary Location')])
        end;
        if not fJ.properties.distance then
            setItem(fJ.properties, "distance", 0)
        end;
        setItem(fJ, "id", number(ref));
        setItem(fJ.properties, "PLACE_ID", number(ref));
        setItem(fJ.properties, "PLACE_NAME", ref.Name);
        setItem(fJ.properties, "PLACE_TYPE", ref.Type.Name);
        setItem(fJ.properties, "PLACE_TYPE_ID", ref.Type.number(Id));
        setItem(J, "features", [fJ])
    else
        void
    end;
    formatJSON(J)
end;

 

Hier ist das Problem in

setItem(J, "features", [fJ])

19 Antworten

null
    • TU Berlin
    • Christoph_dh
    • vor 5 Monaten
    • Gemeldet - anzeigen

    Zum leichteren Verständnis, hier ein korrektes GeoJSON:

    {"type":"FeatureCollection","features":[{"geometry":{"coordinates":[-1.4440324,40.405312],"type":"Point"},"id":28,"properties":{"PLACE_ID":28,"PLACE_NAME":"Albarracín","PLACE_TYPE":"locality","PLACE_TYPE_ID":55,"distance":1180.7024668824363},"type":"Feature"}]}
    
    • TU Berlin
    • Christoph_dh
    • vor 5 Monaten
    • Gemeldet - anzeigen

    Und das void in else ist nicht im Original, hier nur zur Verkürzung. Tatsächlich wird dort dann ein ganzes GeoJSON konstruiert statt aktualisiert.

    • mirko3
    • vor 5 Monaten
    • Gemeldet - anzeigen

    Hi Christoph. Der Datentyp "Places" ist mir nicht bekannt. Wenn du hier mit dem Parameter "ref" ein Objekt übergibst, dann müßte dort "any" stehen und dann wechseln sofort alle Fehlermeldungen in die Tiefe, weil z.B. ref.'Primary Location' vom Typ "any" ist und nicht "location", demzufolge nicht an longitude() übergeben werden kann. Falls ich hier falsch liege, dann schreibe doch noch, welches Format der Parameter "ref" hat. Mirko

      • TU Berlin
      • Christoph_dh
      • vor 5 Monaten
      • Gemeldet - anzeigen

      Danke. Aber das ist, denke ich, leider nicht das Problem. ref ist ein Record aus der Tabelle Places, der an die globale Funktion übergeben wird. Es passiert aber dasselbe (im Client) wenn das Skript lokal definiert wird.

       

      Wie gesagt, die Funktion funktioniert einwandfrei im Browser (in beiden Versionen, lokal und global). Der Client zeigt aber nicht nur einen Fehler, sondern formatiert auch das ganze Skript um, siehe unten.

      (let ref := this; (let fJ := [{}]; (let gJ := {}; (let pJ := {PLACE_ID:number(ref),PLACE_NAME:ref.A,PLACE_TYPE:ref.M.A,PLACE_TYPE_ID:ref.M.number(_id),distance:0}; (let JJ := ref.A2; (let J := parseJSON(JJ); ((if (cnt(J.features) > 0) then ((fJ := [J.first(features)]);(if (((((ref.B and (longitude(ref.B) != 0)) and (latitude(ref.B) != 0)) and longitude(ref.B)) and latitude(ref.B)) and (first(fJ).geometry.type = "Point")) then setItem(first(fJ).geometry,"coordinates",[longitude(ref.B), latitude(ref.B)]) end);(if not first(fJ).properties then setItem(first(fJ),"properties",pJ) end);(if not first(fJ).properties.distance then setItem(first(fJ).properties,"distance",0) end);setItem(first(fJ),"id",number(ref));setItem(first(fJ).properties,"PLACE_ID",number(ref));setItem(first(fJ).properties,"PLACE_NAME",ref.A);setItem(first(fJ).properties,"PLACE_TYPE",ref.M.A);setItem(first(fJ).properties,"PLACE_TYPE_ID",ref.M.number(_id));setItem(J,"features",fJ)) else (if ((((ref.B and (longitude(ref.B) != 0)) and (latitude(ref.B) != 0)) and longitude(ref.B)) and latitude(ref.B)) then ((gJ := {type:"Point",coordinates:[longitude(ref.B), latitude(ref.B)]});(J := {type:"FeatureCollection",features:[{type:"Feature",id:number(ref),geometry:gJ,properties:pJ}]})) end));J)))))))
      

      Ich denke, der parser für das Skript validiert im CLI und im Browser unterschiedlich...

      • mirko3
      • vor 5 Monaten
      • Gemeldet - anzeigen

       Dann tut es mir leid. Hier bin ich überfragt. Vielleicht hat jemand anderes noch Ideen. Mirko

    • Gotje_Ing
    • vor 5 Monaten
    • Gemeldet - anzeigen

    Moin,
    ich habe eine Vermutung. 
    Wenn ich das richtig sehe, möchtest du mit der Zeile hier ein Array auf den Key "features" schreiben.

    setItem(J, "features", [fJ])
    

    Das Problem scheint nun zu sein, dass das object nicht korrekt definiert ist. 
    Versuche bitte mal folgendes. 
    Erstelle das leere Array so:

    let fJ := [{}][< (< {})];

    Dann in der if-Abfrage das Array so beschreiben:

    fJ := array(fJ, [J.first(features)]);

    Dann sollte das in dem setItem so aussehen:

    setItem(J, "features", first(fJ))

    Das ist gerade ziemlich experimentell und ohne die Datenbank ist das schwer nachzuvollziehen. Falles es nicht klappt, kannst du mal eine Testdatenbank hier hochladen?
    Grüße Philipp

      • TU Berlin
      • Christoph_dh
      • vor 5 Monaten
      • Gemeldet - anzeigen

      Super, das adressiert das Problem, denke ich, direkt. Ich werde es ausprobieren. Die Schwierigkeit ist, dass es nur auf dem Client testbar ist (da im Browser ja alles funktioniert) und ich gerade nicht immer Zugang zu einem Mac habe für diese Tests. Ich versuche es alsbald.

      • TU Berlin
      • Christoph_dh
      • vor 5 Monaten
      • Gemeldet - anzeigen


      Es erstellt im Browser leider ein JSON, das features nicht als Liste definiert, was gegen die GeoJSON Spezifikation verstößt:
       

      {"type":"FeatureCollection","features":{"geometry":{"coordinates":[6.083788,50.776207],"type":"Point"},"id":764,"properties":{"PLACE_ID":764,"PLACE_NAME":"Aachen","PLACE_TYPE":"locality","PLACE_TYPE_ID":55,"distance":1102.6045032766513},"type":"Feature"}}
      

      Hier als Aufruf ohne Funktion direkt in Tabelle der ganze Code
       

      let ref := this;
      let fJ := [{}][< (< {})];
      let gJ := {};
      let pJ := {
              PLACE_ID: number(ref),
              PLACE_NAME: ref.Name,
              PLACE_TYPE: ref.Type.Name,
              PLACE_TYPE_ID: ref.Type.number(Id),
              distance: 0
          };
      let JJ := ref.GeoJSON;
      let J := parseJSON(JJ);
      if cnt(J.features) > 0 then
          fJ := array(fJ, [J.first(features)]);
          if ref.'Primary Location' and longitude(ref.'Primary Location') != 0 and
                          latitude(ref.'Primary Location') != 0 and
                      longitude(ref.'Primary Location') and
                  latitude(ref.'Primary Location') and
              first(fJ).geometry.type = "Point" then
              setItem(first(fJ).geometry, "coordinates", [longitude(ref.'Primary Location'), latitude(ref.'Primary Location')])
          end;
          if not first(fJ).properties then
              setItem(first(fJ), "properties", pJ)
          end;
          if not first(fJ).properties.distance then
              setItem(first(fJ).properties, "distance", 0)
          end;
          setItem(first(fJ), "id", number(ref));
          setItem(first(fJ).properties, "PLACE_ID", number(ref));
          setItem(first(fJ).properties, "PLACE_NAME", ref.Name);
          setItem(first(fJ).properties, "PLACE_TYPE", ref.Type.Name);
          setItem(first(fJ).properties, "PLACE_TYPE_ID", ref.Type.number(Id));
          setItem(J, "features", first(fJ))
      else
          if ref.'Primary Location' and longitude(ref.'Primary Location') != 0 and
                      latitude(ref.'Primary Location') != 0 and
                  longitude(ref.'Primary Location') and
              latitude(ref.'Primary Location') then
              gJ := {
                      type: "Point",
                      coordinates: [longitude(ref.'Primary Location'), latitude(ref.'Primary Location')]
                  };
              J := {
                      type: "FeatureCollection",
                      features: [{
                              type: "Feature",
                              id: number(ref),
                              geometry: gJ,
                              properties: pJ
                          }]
                  }
          end
      end;
      J
      
      • Gotje_Ing
      • vor 5 Monaten
      • Gemeldet - anzeigen

       Klappt das vielleicht mit 
       

      setItem(J, "features", [first(fJ)])
      
      
      • TU Berlin
      • Christoph_dh
      • vor 5 Monaten
      • Gemeldet - anzeigen

      Ja!!
      Jetzt bleibt nur noch, ob auch der Client dies korrekt parst. Anbei mal ein Minimaltest (ich kann nicht garantieren, dass sich der Fehler im CLI hier reproduzieren lässt)

    • TU Berlin
    • Christoph_dh
    • vor 5 Monaten
    • Gemeldet - anzeigen

    Nein, auch das löst das Problem im Client nicht!

    grafik

    Im Browser geht es!
    grafik

      • Gotje_Ing
      • vor 5 Monaten
      • Gemeldet - anzeigen

      Dann bin ich raus, das ist etwas für den Support.

      • TU Berlin
      • Christoph_dh
      • vor 5 Monaten
      • Gemeldet - anzeigen

       😂Ja, leider...

    • UweG
    • vor 5 Monaten
    • Gemeldet - anzeigen

    Tests haben ergeben, dass der Fehler bei der Mac-App-Version unter v3.10.x auftritt.
    Ein Update auf eine aktuelle Version sollte es beheben.
    Ist die Nutzung einer Version unter 3.10.x zwingend notwendig, sollte folgendes Script funktionieren.

    let ref := this;
    let fJ := [{}][< (< {})];
    let gJ := {};
    let pJ := {
            PLACE_ID: number(ref),
    PLACE_NAME: ref.Name,
            distance: 0
        };
    let JJ := ref.GeoJSON;
    let J := parseJSON(JJ);
    if cnt(J.features) > 0 then
        fJ := array(fJ, [J.first(features)]);
        if not first(fJ).properties then
            setItem(first(fJ), "properties", pJ)
        end;
        if not first(fJ).properties.distance then
            setItem(first(fJ).properties, "distance", 0)
        end;
        setItem(first(fJ), "id", number(ref));
        setItem(first(fJ).properties, "PLACE_ID", number(ref));
        setItem(first(fJ).properties, "PLACE_NAME", "UweG");
        setItem(J, "features", [first(fJ)])
    end;
    if cnt(J.features) < 1 then
        gJ := {
                type: "Point",
                coordinates: [0, 0]
            };
        J := {
                type: "FeatureCollection",
                features: [{
                        type: "Feature",
                        id: number(ref),
                        geometry: gJ,
                        properties: pJ
                    }]
            }
    end;
    J

      • TU Berlin
      • Christoph_dh
      • vor 5 Monaten
      • Gemeldet - anzeigen

      Das hat das Problem gelöst. Besonders überrascht mich die Änderung im Skript zur Kompatibilität mit der alten Version. Danke in jedem Fall!

      • ninox_lerepublicain
      • vor 5 Monaten
      • Gemeldet - anzeigen

       Hello Uwe, can you explain what is do the line : let fJ := [{}][< (< {})];

      specialy the caractar : <

      I think you initialize a array of object, but after i dont understant. Thank you for your sharing. 

      Have a nice day!

      Robert

      • TU Berlin
      • Christoph_dh
      • vor 5 Monaten
      • Gemeldet - anzeigen

      we should ask

      I was puzzled too. Seems to be a filter statement. However, I don't think that it is strictly needed, but I might be wrong.

      • Gotje_Ing
      • vor 5 Monaten
      • Gemeldet - anzeigen

       

      Basically, if you want to create an empty array, or in other words a variable without any content but assign it to be an array from the start, you have limited options depending on the data type. For completeness, here are the other ones in a way that you can test in a formula field:
      Numbers:

      let test := [0][!= 0];
      let array := array(test, [1]);
      debugValueInfo(array)

      Records:

      let test := [record(Tabelle1,0)][!= null];
      let array := array(test, [record(Tabelle1,1)]);
      debugValueInfo(array)

      Strings:

      let test := ["0"][!= "0"];
      let array := array(test, ["string"]);
      debugValueInfo(array)

      Objects:

      let test := [{}][< (< {})];
      let array := array(test, [{ key: "value" }]);
      debugValueInfo(array)


      The one for objects stands out, but the < (< part comes from ninox directly. If you input 

      let test := [{}][!={}];

      it gets converted into:

      let test := [{}][< (< {})];

      Thats some black magic going on, but it works the same. 

      The common theme in all of them is, to create a variable as normal, but filter out the item we just set as the first item. Therefore, the data type is assigned but the content is removed. From now on, this variable will behave like an array and you can assign any fitting type to it, with the array([],[]) function in ninox. 
      This comes in handy, if you want to for example fill an array with record ids in a for-loop but dont know the first element beforehand. There is one other way to do it for records, which is longer and you have to know the table the records need to be from, which looks like this:

      unique(record('Table 1',0))[!= 0];

      Same idea here, unique creates an array, the first entry is set to be record number 0 and is filtered out instantly again. 

      Hope that clears things up. 

    • UweG
    • vor 5 Monaten
    • Gemeldet - anzeigen

    Das selbe bekommt man auch wie folgt hin:
    let arrObj := [{}];
    arrObj := null

    let arrTxt := [""];
    arrTxt := null
    etc.

    Man definiert den Type eines Arrays und hat anschließend ein leeres Array mit dem man arbeiten kann.
    Ist halt eine Zeile mehr Script