Thorsten: JSON nach Vorlage erstellen

Hallo,

ich möchte einen JSON-String erstellen, weiß aber nicht genau, wie.

Ich frage meine DB ab:

SELECT Year(FROM_UNIXTIME(MTimestamp)) AS Jahr, Month(FROM_UNIXTIME(MTimestamp)) AS Monat, COUNT(MID) AS Anzahl 
FROM table
WHERE Year(FROM_UNIXTIME(MTimestamp)) = 2015
GROUP BY Year(FROM_UNIXTIME(MTimestamp)),Month(FROM_UNIXTIME(MTimestamp))

Ergebnismenge dieser Abfrage ist so etwas in dieser Art:

Jahr 	Monat 	Anzahl 
2015 	1 	1
2015 	2 	2
2015 	3 	2
2015 	4 	1
2015 	5 	9
2015 	7 	3
2015 	10 	4
2015 	11 	3

Meine Frage: Wie verpacke ich dieses Ergebnis mit php so, dass folgendes Ergebnis an meine JS Anwendung gesendet wird:


{
  "2015": [1,2,2,1,9,0,3,0,0,4,3]
}

Hinweis: Trotz der nur 8 Werte in der Ergebnismenge würde ich gerne die fehlenden Werte innerhalb der Reihe mit einer 0 auffüllen.

Wie generiere ich in php meinen JSON-String? Ich komme mit der json_encode Methode nicht zurecht. Ich habe sie zwar schon früher benutzt, aber ich schaffs nicht, obigen String 1:1 zu generieren.

Thorsten

  1. Moin!

    Ergebnismenge dieser Abfrage ist so etwas in dieser Art:

    Jahr 	Monat 	Anzahl 
    2015 	1 	1
    2015 	2 	2
    2015 	3 	2
    2015 	4 	1
    2015 	5 	9
    2015 	7 	3
    2015 	10 	4
    2015 	11 	3
    

    Meine Frage: Wie verpacke ich dieses Ergebnis mit php so, dass folgendes Ergebnis an meine JS Anwendung gesendet wird:

    {
      "2015": [1,2,2,1,9,0,3,0,0,4,3]
    }
    
    

    Immer schön dumm stellen. DAS ist die Kunst des Programmierens!

    $Jahr_min = 9999999;
    $Jahr_max = -9999999;
    
    # Deine Abfrage
    # Dann für jede Zeile der Rückgabe ($ret)
        # Frühestes Jahr nach einer uralten Methode ermitteln:
        if ( $ret['Jahr'] < $Jahr_min ) { $Jahr_min = $ret['Jahr']; }
        # Spätestes Jahr nach der selben uralten Methode ermitteln:
        if ( $ret['Jahr'] > $Jahr_man ) { $Jahr_man = $ret['Jahr']; }
        # im gewünschten JSON beginnen die Monate offenbar bei 0:
        $ar[$ret['Jahr']][$ret['Monat'] - 1]=$ret['Anzahl'];
    
    #dann:
    # für jedes Jahr
    for ( $Jahr=$Jahr_min; $Jahr<=$Jahr_max;$Jahr++ ) {
        #für jeden Monat:
        for ($Monat=1; $Monat <= 12; $Monat++) {
             if ( empty( $ar[$Jahr][$Monat])) { $ar[$Jahr][$Monat] = 0; }
        }
    }
    
    # und jetzt das json "bauen";
    
    $json=json_encode($ar, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT);
    

    Das ist ein Plan, kein fertiges Programm.

    Jörg Reinholz

    1. Hallo,

      {
        "2015": [1,2,2,1,9,0,3,0,0,4,3]
      }
      
      # im gewünschten JSON beginnen die Monate offenbar bei 0:
      

      Offenbar? Man kann auch vermuten, sein Jahr besteht aus elf Monaten.

      Gruß
      Kalk

    2. Hi Jörg,

      Immer schön dumm stellen. DAS ist die Kunst des Programmierens!

      Da ist was dran :-)

      # im gewünschten JSON beginnen die Monate offenbar bei 0:
      

      Nein, in 2015 können erst 11 Einträge vorhanden sein, da der Dez. noch nicht begonnen hat.

      und jetzt das json "bauen";

      $json=json_encode($ar, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT);

      Kannst Du mir noch 1-2 Worte zu den Parametern sagen, die Du benutzt hast?

      Das ist ein Plan, kein fertiges Programm.

      Guter Plan, damit kann ich was anfangen. Dank' Dir

      Thorsten

      1. Moin!

        Kannst Du mir noch 1-2 Worte zu den Parametern sagen, die Du benutzt hast?

        Wenn ich richtig liege sorgt JSON_NUMERIC_CHECK dafür, dass numerische Werte als solche im JSON landen (also nicht "2015" sondern 2015) und JSON_PRETTY_PRINT dafür, dass Zeilenumbrüche und Leerzeichen eingefügt werden. Notiere was Du davon brauchst mit einem PIPE-Symbol, die werden binär addiert:

        $json=json_encode($ar, JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
        

        Lass Dir das einfach mal ausgeben:

        print "<pre>", json_encode($ar, JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
        exit;
        

        Jörg Reinholz

      2. Moin!

        # im gewünschten JSON beginnen die Monate offenbar bei 0:
        

        Nein, in 2015 können erst 11 Einträge vorhanden sein, da der Dez. noch nicht begonnen hat.

        Du übergibst in Deinem JSON-Beispiel aber eine Liste:

        {
          "2015": [1,2,2,1,9,0,3,0,0,4,3]
        }
        

        Die beginnt immer mit dem Element Nummer 0 (Januar). Der Dezember ist dann Monat Nummer 11. Das passt aber auch sehr gut zu Javascript, welches sich genau so verhält. Ich würde dabei bleiben.

        Jörg Reinholz

      3. Moin!

        <?php
        
        # Simulation Datenbank:
        $ar[1]=1;
        $ar[2]=2;
        $ar[3]=2;
        $ar[4]=1;
        $ar[5]=9;
        #$ar[6]=0;
        $ar[7]=3;
        #$ar[8]=0;
        #$ar[9]=0;
        $ar[10]=4;
        $ar[11]=3;
        #$ar[12]=0;
        
        $a[2015]=$ar;
        
        $Jahr_min = 2015;
        $Jahr_max = 2015;
        
        # Auffüllen der Monate
        
        for ( $Jahr=$Jahr_min; $Jahr<=$Jahr_max;$Jahr++ ) {
            #für jeden Monat:
            for ($Monat=1; $Monat <= 12; $Monat++) {
                if ( empty( $ar[$Monat])) { $ar[$Monat] = 0; }
                $a[$Jahr][$Monat-1]=$ar[$Monat];
            }
        }
        
        #Erzeugen des JSON-Strings:
        echo json_encode($a, JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
        echo "\n";
        
        
        #Erzeugen des JSON-Strings - manuell:
        $Jahr=2015;
        #für jeden Monat:
        $s = "{\n\"" . $Jahr . '":[';
        
        for ($Monat=0; $Monat < 12; $Monat++) {
            $s .= $a[$Jahr][$Monat];
            if ( 11 != $Monat ) {
                $s .= ',';
            }
        }
        $s .= "]\n}";
        echo $s;
        echo "\n";
        
        

        Man kann sich, wie Cheatah vor Jahren hier schon einmal anmerkte, auf die Reihenfolge der Elemente eines Arrays nicht wirklich verlassen. (Auf die Indizes aber schon...) Der mit json_encode erzeugte String sieht zwar optisch nicht so aus wie Dein JSON:

        {
            "2015": {
                "1": 1,
                "2": 2,
                "3": 2,
                "4": 1,
                "5": 9,
                "7": 3,
                "10": 4,
                "11": 3,
                "6": 0,
                "8": 0,
                "9": 0,
                "12": 0
            }
        }
        

        hat aber praktisch den gleichen Inhalt. Ansonsten kann man manuell einen String basteln...

        {
        "2015":[1,2,2,1,9,0,3,0,0,4,3,0]
        }
        

        Jörg Reinholz

        1. Moin!

          Man-oh-Mann. Die Monate wie in der natürlichen Ausdrucksweise mit 1 zu beginnen bringt beim Programmieren aber gut durcheinander, die kurze Zeit fürs Editieren tut dann ihr Übriges …

          Also, der JSON-String sieht so aus:

          {
              "2015": {
                  "1": 2,
                  "2": 2,
                  "3": 1,
                  "4": 9,
                  "5": 0,
                  "7": 0,
                  "10": 3,
                  "11": 0,
                  "0": 1,
                  "6": 3,
                  "8": 0,
                  "9": 4
              }
          }
          

          und hat also praktisch den gleichen Inhalt wie:

          {
          "2015":[1,2,2,1,9,0,3,0,0,4,3,0]
          }
          

          Jörg Reinholz

        2. <?php
          
          # Simulation Datenbank:
          $ar[1]=1;
          $ar[2]=2;
          $ar[3]=2;
          $ar[4]=1;
          $ar[5]=9;
          #$ar[6]=0;
          $ar[7]=3;
          #$ar[8]=0;
          #$ar[9]=0;
          $ar[10]=4;
          $ar[11]=3;
          #$ar[12]=0;
          
          $a[2015]=$ar;
          
          $Jahr_min = 2015;
          $Jahr_max = 2015;
          
          # Auffüllen der Monate
          
          for ( $Jahr=$Jahr_min; $Jahr<=$Jahr_max;$Jahr++ ) {
              #für jeden Monat:
              for ($Monat=1; $Monat <= 12; $Monat++) {
                  if ( empty( $ar[$Monat])) { $ar[$Monat] = 0; }
                  $a[$Jahr][$Monat-1]=$ar[$Monat];
              }
          }
          
          #Erzeugen des JSON-Strings:
          echo json_encode($a, JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
          echo "\n";
          
          
          #Erzeugen des JSON-Strings - manuell:
          $Jahr=2015;
          #für jeden Monat:
          $s = "{\n\"" . $Jahr . '":[';
          
          for ($Monat=0; $Monat < 12; $Monat++) {
              $s .= $a[$Jahr][$Monat];
              if ( 11 != $Monat ) {
                  $s .= ',';
              }
          }
          $s .= "]\n}";
          echo $s;
          echo "\n";
          
          

          Man kann sich, wie Cheatah vor Jahren hier schon einmal anmerkte, auf die Reihenfolge der Elemente eines Arrays nicht wirklich verlassen. (Auf die Indizes aber schon...) Der mit json_encode erzeugte String sieht zwar optisch nicht so aus wie Dein JSON:

          {
              "2015": {
                  "1": 1,
                  "2": 2,
                  "3": 2,
                  "4": 1,
                  "5": 9,
                  "7": 3,
                  "10": 4,
                  "11": 3,
                  "6": 0,
                  "8": 0,
                  "9": 0,
                  "12": 0
              }
          }
          

          hat aber praktisch den gleichen Inhalt. Ansonsten kann man manuell einen String basteln...

          {
          "2015":[1,2,2,1,9,0,3,0,0,4,3,0]
          }
          

          Hallo Jörg,

          erstmal dankeschön für Deine "Spielerei". Ich antworte hier in diesem Post, auch wenn ich damit Deine Arrayanfangskorrektur mal außen vor lasse, damit ich den Quelltext zitieren kann.

          Ich frage mich gerade, warum Du da so kompliziert heran gehst?

          $jahrmin und -max benötigen wir eigentlich nicht, da bereits durch die Query vorgegeben. Ich stimme mit Dir und Cheatah überein, dass die Indizes verlässlich sind und wir sie auch nutzen sollten. Das Array (nicht das gebastelte, sondern das erste) wird von meiner JS-Anwendung nicht akzeptiert. Ist zwar gültiges JSON, aber die Anwendung will explizit nur mit der vorgegebenen Notierung arbeiten. Du brauchst den String aber gar nicht manuell zu basteln, das geht auch mittels Schleife.

          Ich habs mal hier ähnlich Deiner Vorgehensweise angepasst.

          # Simulation Datenbank:
          $arr[0]=130;
          #$arr[1]=0;
          $arr[2]=50;
          #$arr[3]=0;
          $arr[4]=160;
          $arr[5]=150;
          $arr[6]=130;
          $arr[7]=120;
          #$arr[8]=0;
          $arr[9]=40;
          $arr[10]=160;
          $arr[11]=350;
          
          $jahr = 2015;
          
          for ($Monat=0; $Monat <= 11; $Monat++) {
          if (empty($arr[$Monat])) {
          	$innenarray[$Monat] = 0;
          } else {
            	$innenarray[$Monat] = $arr[$Monat];
          }
          }
          
          $data = array($jahr => $innenarray);
          echo json_encode($data);
          

          ergibt:

          {"2015":[130,0,50,0,160,150,130,120,0,40,160,350]}
          

          Oder habe ich was übersehen oder nicht bedacht?

          Thorsten

          1. Moin!

            Ich frage mich gerade, warum Du da so kompliziert heran gehst?

            War schon spät ...

            $jahrmin und -max benötigen wir eigentlich nicht, da bereits durch die Query vorgegeben.

            Bezüglich des Jahrs war nichts definiert. Dass das Jahr aber im JSON auftaucht ist eigentlich ein Merkmal dafür, dass es variabel ist.

            Oder habe ich was übersehen oder nicht bedacht?

            Möglicherweise. Deiner ersten Darstellungen nach begannen die Monate mit 1 (=Januar) zu zählen. Dann musst Du natürlich anpassen:

            <?php
            
            # Konfiguration
            $mOffset=1 # 1, wenn Monate in der DB mit 1 = Januar gezählt werden
                       # 0, wenn Monate in der DB mit 0 = Januar gezählt werden
            # Simulation Datenbank:
            $arr[1]=130;
            #$arr[2]=0;
            $arr[3]=50;
            #$arr[4]=0;
            $arr[5]=160;
            $arr[6]=150;
            $arr[7]=130;
            $arr[8]=120;
            #$arr[9]=0;
            $arr[10]=40;
            $arr[11]=160;
            $arr[12]=350;
            
            $jahr = 2015;
            
            for ($Monat=0; $Monat < 12; $Monat++) { 
            # < 12 statt <= 11 sorgt für mehr Klarheit und spart einen Vergleich ...
                if (empty($arr[$Monat + $mOffset])) {
                        $innenarray[$Monat] = 0;
                } else {
                        $innenarray[$Monat] = $arr[$Monat + $mOffset];
                }
            }
            $data = array($jahr => $innenarray);
            echo json_encode($data);
            

            Jörg Reinholz

            1. Hi,

              Bezüglich des Jahrs war nichts definiert. Dass das Jahr aber im JSON auftaucht ist eigentlich ein Merkmal dafür, dass es variabel ist.

              Ist es auch. Das regelt aber der Scriptaufrufen bereits über einen Parameter, der in die WHERE-Bedingung der SQL-Query einfließt.

              Möglicherweise. Deiner ersten Darstellungen nach begannen die Monate mit 1 (=Januar) zu zählen. Dann musst Du natürlich anpassen:

              Würde ich. Aber eine Monate begannen immer mit 0=Januar (oder habe ich mich irgendwo verschrieben?), in miener ersten Darstellung gabs lediglich mal nur 11 Werte. Das lag aber daran, dass er 2015er Werte waren und der Dezemberwert nicht vorliegt.

              Danke jedenfalls für Deine Hilfe, jetzt läuft alles ganz prima.

              Thorsten

              <?php
              
              # Konfiguration
              $mOffset=1 # 1, wenn Monate in der DB mit 1 = Januar gezählt werden
                         # 0, wenn Monate in der DB mit 0 = Januar gezählt werden
              # Simulation Datenbank:
              $arr[1]=130;
              #$arr[2]=0;
              $arr[3]=50;
              #$arr[4]=0;
              $arr[5]=160;
              $arr[6]=150;
              $arr[7]=130;
              $arr[8]=120;
              #$arr[9]=0;
              $arr[10]=40;
              $arr[11]=160;
              $arr[12]=350;
              
              $jahr = 2015;
              
              for ($Monat=0; $Monat < 12; $Monat++) { 
              # < 12 statt <= 11 sorgt für mehr Klarheit und spart einen Vergleich ...
                  if (empty($arr[$Monat + $mOffset])) {
                          $innenarray[$Monat] = 0;
                  } else {
                          $innenarray[$Monat] = $arr[$Monat + $mOffset];
                  }
              }
              $data = array($jahr => $innenarray);
              echo json_encode($data);
              

              Jörg Reinholz

  2. Hallo,

    Hinweis: Trotz der nur 8 Werte in der Ergebnismenge würde ich gerne die fehlenden Werte innerhalb der Reihe mit einer 0 auffüllen.

    Naheliegend wäre eine For-Schleife, die über alle Monate iteriert und den entsprechenden Wert abfragt. Vielleicht gibts was kürzeres mit einem Array-Vergleich.

    Gruß
    Kalk

    1. Hallo,

      Vielleicht gibts was kürzeres mit einem Array-Vergleich.

      schau dir mal array_merge an.

      Gruß
      Kalk

  3. Hinweis: Trotz der nur 8 Werte in der Ergebnismenge würde ich gerne die fehlenden Werte innerhalb der Reihe mit einer 0 auffüllen.

    Tipp: Nein, nicht auffüllen. Benutze besser einen namentlichen Index, z.B. ['jjjj.mm'] oder ['jjjj']['mm']

    Wie generiere ich in php meinen JSON-String? Ich komme mit der json_encode Methode nicht zurecht.

    Ich denke, der Methode übergibst Du das nach o.g. Tipp erstellte Array.

    1. Hi,

      Tipp: Nein, nicht auffüllen. Benutze besser einen namentlichen Index, z.B. ['jjjj.mm'] oder ['jjjj']['mm']

      Nein, "aufnullen" passt hier schon, weil ich ein Chart erstellen möchte. Und wenn in einem Monat kein Eintrag vorhanden ist., soll tatsächlich der Graph auf 0 gehen.

      Thorsten