PHP: JSON in CSV konvertieren

Status
Dieses Thema wurde gelöst! Zur Lösung gehen…

canju

Erfahrenes Mitglied
Hallo ihr Lieben,

ich bin hier gerade am verweifeln und hoffe ihr könnt mir (mal) wieder helfen.

Ich lade mir über einen api call via basic auth eine .json datei auf einen ftp.
Ich möchte diese .json Datei jetzt mit PHP in eine .csv Datei convertieren. Ich kriege das einfach nicht hin (ich würde am liebsten gerade in den Tisch beißen). Habe schon einige Scripte vergebens ausprobiert. meist ist der Fehler, dass nach dem json_decode kein Array vorliegt.

Der Inhalt der .json Datei sieht so aus:

JSON:
{
   "status_code":200,
   "status_message":"OK",
   "data":[
      {
         "id":1234565,
         "status":"confirmed",
         "amount_net":453,
         "created":"2019-10-15 12:00:00",
         "tracking":"rrkkdje3-343-3434",
         "product":"car",
         "gclid":null,
         "referer_url":"",
         "cancel_reason":null
      },
      {
         "id":9876363,
         "status":"verg\u00fctet",
         "amount_net":13,
         "created":"2019-10-19 12:00:00",
         "tracking":"0984dd-e9934",
         "insurance":"boat",
         "gclid":null,
         "referer_url":"",
         "cancel_reason":null
      }
   ]
}

Ich bräuchte jetzt die "Datensätze" innerhalb von "data":[...] jeweils als eigene Zeile in der .csv Datei. Inkl. Überschriften.

Könte mir evtl. einer von euch Profis mit einem kleinen PHP Snippet das die json datei in csv konvertiert aushelfen?

Grüße,
Canju
 
meist ist der Fehler, dass nach dem json_decode kein Array vorliegt.
Das kann zwei Gründe haben:
PHP: json_decode - Manual

1. Das Array liegt ja innerhalb unter dem Schlüssel "data", d. h. Du musst erst dieses Array heraus holen:
$arr = $json_decoded['data'];
2. json_decode kann entweder ein assoziatives Array oder ein Objekt zurück geben, wobei, wenn ich mich richtig erinnere, letzteres Default ist. Setze den zweiten Parameter auf true, dann wirst Du ein Array zurück bekommen.

Edit: U. u. kann ein 3. Grund sein, dass das JSON nicht geparst werden kann, weil es fehlerhaft ist. Dann liefert json_decode null zurück.
 
Hi Sempervivum,
danke für deine Antort. Das json war bereits valide aber das:
... d. h. Du musst erst dieses Array heraus holen: ...
hat mich bei meiner Recherche weitergebracht.

Das Erstellen der csv und das Einfügen der Daten funktioniert jetzt:
PHP:
<?php
// PROCESS THE JSON FILE
$content = file_get_contents($path); // get the content of downlaoded json file
$list = json_decode($content, true); // decode json into array
$data = $list['data']; //get desired data

$separator = ';';
$enclosure = '"';
$column_names = array_keys($data[0]);

$fp = fopen('file7.csv', 'w');


fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF)); // set file encoding to UTF-8
fputcsv($fp, $column_names, $separator, $enclosure); // write column names
    foreach ($data as $fields) {
        fputcsv($fp, $fields, $separator, $enclosure); // write rows
    }

fclose($fp);
?>
Falls noch jemand einen eleganteren/ sicheren Weg kennt - bin offen für Ratschläge.

Ich möchte noch Fehler und Erfolgsmeldungen beim Öffnen / Schreiben und Schließen der Datei in eine separate Logdatei schreiben. Kann mir hierzu jemand behilflich sein?
 
Hi,
sowas würde ich über try and catch realisieren:

PHP:
try{
    if (!file_exists($path)) {
        $content = file_get_contents($path); // get the content of downlaoded json file
    } else {
        throw new Exception("File was not found");
    }

    $list = json_decode($content, true); // decode json into array
    $data = $list['data']; //get desired data

    $separator = ';';
    $enclosure = '"';
    $column_names = array_keys($data[0]);

    $fp = fopen('file7.csv', 'w');

    fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF)); // set file encoding to UTF-8
    fputcsv($fp, $column_names, $separator, $enclosure); // write column names
    foreach ($data as $fields) {
        fputcsv($fp, $fields, $separator, $enclosure); // write rows
    }

    fclose($fp);

} catch ( Exception $e ) {
   echo 'Message: ' .$e->getMessage();
}

Also so im Grunde. Und Du solltest immer erst überprüfen ob eine Datei existiert wenn die auf diese Zugreifst oder liest.

Grüße
 
Hey Jan-Frederik Stieler,

sorry für das späte Feedback. Ich hab das mit try and catch probiert, aber bekomme immer die Fehlermeldung "File was not found" ausgegeben, obwohl die Datei vorhanden ist. Habe bestimmt 1h dran rumprobiert und die Verzechnisse gecheckt, die waren auf jeden fall korrekt. Wenn ich es ohne try and catch laufen lasse funzt es einwandfrei

Ich habe es jetzt so gelöst:

PHP:
        if(!fputcsv($fp, $fields, $separator, $enclosure))
            die($current_datetime . " - ". $script_name . " - File creation failed \n"); // write rows
    } echo $current_datetime . " - ". $script_name . " - File creation successfull \n";

Ich stehe jetzt vor der nächsten Herausforderung:
Ich hole mir wieder eine json datei via curl (bearer token) und lege diese in ein Verzeichnis auf dem Server ab. Das klappt auch wunderbar, nur der Endpunkt lässt nur einen Zeitraum von max 30 Tagen pro request zu und maximal 20 API request pro Minute. Ich muss mir aber jedesmal (cron 1x täglich) mindestens die letzten 12 Monate holen, um die rücwirkende Änderung zu bekommen.

Das ist der Code den ich zum herunterladen und speichern der json datei verwende:

PHP:
<?php
/*** DOWNLOAD FILE FROM CURL ****/
$publisher_id = 'xxx';
$start_date = '2020-02-26';
$end_date = '2020-03-08';
$bearer_token = 'token';
//API KEY AND PASSWORD
$headers = array("Authorization: Bearer $bearer_token", 'Content-Type: application/json');

$current_datetime = date("Y-m-d H:i:s");
$script_name = $_SERVER['SCRIPT_NAME'];

//URL
$url= 'https://api.example.com/' . $publisher_id . '/transactions/?startDate=' . $start_date . 'T00%3A00%3A00&endDate=' . $end_date . 'T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction';

//FILE NAME
$filename = 'file.json';

//DOWNLOAD PATH
$path = '/download/pfad/'.$filename;

//FOLDER PATH
$fp = fopen($path, 'w');

//SETTING UP CURL REQUEST
$ch = curl_init($url) or die($current_datetime . " - ". $script_name . " - Couldnt reach endpoint \n");
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$data = curl_exec($ch);

//CONNECTION CLOSE
curl_close($ch);
fclose($fp);
?>

Beispiel response:

JSON:
[
  {
    "id": 259630312,
    "url": "http://www.publisher.com",
    "advertiserId": 7052,
    "publisherId": 189069,
    "commissionSharingPublisherId": 55555,
    "commissionSharingSelectedRatePublisherId": 189069,
    "campaign": "campaign name"
    "siteName": "Publisher",
    "commissionStatus": "pending",
    "commissionAmount": {
      "amount": 5.59,
      "currency": "GBP"
    },
    "saleAmount": {
      "amount": 55.96,
      "currency": "GBP"
    },
    "ipHash": "-66667778889991112223",
    "customerCountry": "GB",
    "clickRefs": {
      "clickRef": "12345",
      "clickRef2": "22222",
      "clickRef3": "33333",
      "clickRef4": "44444",
      "clickRef5": "55555",
      "clickRef6": "66666"
    },
    "clickDate": "2017-01-23T12:18:00",
    "transactionDate": "2017-02-20T22:04:00",
    "validationDate": null,
    "type": "Commission group transaction",
    "declineReason": null,
    "voucherCodeUsed": true,
    "voucherCode": "example123",
    "lapseTime": 2454307,
    "amended": false,
    "amendReason": null,
    "oldSaleAmount": null,
    "oldCommissionAmount": null,
    "clickDevice": "Windows",
    "transactionDevice": "Windows",
    "publisherUrl": "http://www.publisher.com/search?query=dvds",
    "advertiserCountry": "GB",
    "orderRef": "111222333444",
    "customParameters": [
      {
        "key": "1",
        "value": "555666"
      },
      {
        "key": "2",
        "value": "example entry"
      },
      {
        "key": "3",
        "value": "LLLMMMNNN"
      }
    ],
    "transactionParts": [
      {
        "commissionGroupId": 12345,
        "amount": 44.76,
        "commissionAmount": 4.50,
        "commissionGroupCode": "DEFAULT",
        "commissionGroupName": "Default Commission"
      }
 
      {
 
        "commissionGroupId": 654321,
        "amount": 11.20,
        "commissionAmount": 1.50,
        "commissionGroupCode": "EXISTING",
        "commissionGroupName": "EXISTING"
      }
 
 
    ],
    "paidToPublisher": false,
    "paymentId": 0,
    "transactionQueryId": 0,
    "originalSaleAmount": null
  },
    "basketProducts": null
}]

Wie bekomme ich das hin, dass ich:

a) die letzten 12 Monate mit jedem API request erhalte - max Zeitraum pro request 30 Tage,
b) dabei nur maximal 20 API requests pro Minute auslöse und
c) die kompletten daten in eine Datei bekomme (alles am Ende in eine .csv Datei zu schreiben wäre auch in ordnung)

Wäre sehr dankbar für eure Unterstützung.

Grüße,
canju
 
Du schreibst nichts davon, dass es einen Mindestabstand zwischen den Requests gibt. Du würdest 13 Requests brauchen, das sind weniger als die 20 möglichen pro Minute. Dann müsste es funktionieren, diese Requests in einer Schleife direkt hintereinander abzusetzen und die Ergebnisse zu sammeln.
Wobei ich das so verstehe, dass n Datensätze mit jedem Request geholt werden und Du das JSON nur auf einen reduziert hast.
 
Direkt meine Stärke ist PHP auch nicht, daher möchte ich ungern ungetesteten Code posten. Ich bereite mal etwas vor und teste es ...
 
So, mein Testcode sieht so aus:
Code:
$bearer_token = 'token';
//API KEY AND PASSWORD
$headers = array("Authorization: Bearer $bearer_token", 'Content-Type: application/json');

$current_datetime = date("Y-m-d H:i:s");
$script_name = $_SERVER['SCRIPT_NAME'];

$dateObj = new DateTime();
$dateObj->modify('-12 months');
$responseArr = [];
for ($i = 0; $i < 13; $i++) {
    // prepare start and end time and URL:
    $start_date = $dateObj->format('Y-m-d');
    $dateObj->modify('+30 days');
    $end_date = $dateObj->format('Y-m-d');
    $url = 'https://meine-text-domain.de/testget-json.php?startDate=' . $start_date . 'T00%3A00%3A00&endDate=' . $end_date . 'T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction';
    echo 'url=' . $url . '<br>';

    // fetch from API by use of curl:
    $ch = curl_init($url) or die($current_datetime . " - " . $script_name . " - Couldnt reach endpoint \n");
    // assign response to variable:
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    $data = curl_exec($ch);
    echo 'response=' . $data . '<br>';
    $data = json_decode($data, true);
    // add response to array:
    $responseArr[] = $data;
}
echo ('<pre>');
var_dump($responseArr);
echo ('<pre>');
// write array to file in JSON format:
file_put_contents('test-get-json.json', json_encode($responseArr, JSON_PRETTY_PRINT));
Der Code schreibt nicht direkt in die Datei sondern sammelt zunächst die Daten und schreibt sie dann in eine einzige Datei.

Die durch curl abgefragte Testdatei testget-json.php gibt einfach nur die GET-Parameter JSON-kodiert aus:
Code:
<?php
echo json_encode($_GET);
Das Ergebnis sieht dann so aus:
Code:
[
    {
        "startDate": "2020-07-21T00:00:00",
        "endDate": "2020-08-20T00:00:00",
        "timezone": "Europe\/Berlin",
        "dateType": "transaction"
    },
    {
        "startDate": "2020-08-20T00:00:00",
        "endDate": "2020-09-19T00:00:00",
        "timezone": "Europe\/Berlin",
        "dateType": "transaction"
    },
    {
        "startDate": "2020-09-19T00:00:00",
        "endDate": "2020-10-19T00:00:00",
        "timezone": "Europe\/Berlin",
        "dateType": "transaction"
    },
usw.

Feinheiten habe ich erst Mal außen vor gelassen: Mit 13 * 30 Tagen schießt man etwas übers Ziel hinaus, unklar, ob Start und Endedatum einbezogen oder ausgeschlossen sind (wahrscheinlich ersteres) etc.
 
Wooow. Vielen lieben Dank für deine Zeit. Das sieht schonmal sehr gut aus, habs mal durch gejagt. Nur ab Zeile 118 weicht die reponse stark von dem Vorangegangenem ab.

Ich habe die json response wieder durch die beispiel reponse ersetzt, aber genau hier erscheinen dann auch die Produktivdaten. Nicht wundern es ist korrekt, dass für manche Monate keine Daten vorhanden sind:


JSON:
"url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2020-07-21T00%3A00%3A00&endDate=2020-08-20T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response=[
  
]"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2020-08-20T00%3A00%3A00&endDate=2020-09-19T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response=[
  
]"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2020-09-19T00%3A00%3A00&endDate=2020-10-19T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response=[
  
]"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2020-10-19T00%3A00%3A00&endDate=2020-11-18T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response=[
  
]"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2020-11-18T00%3A00%3A00&endDate=2020-12-18T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response=[
{
    "id": 259630312,
    "url": "http://www.publisher.com",
    "advertiserId": 7052,
    "publisherId": 189069,
    "commissionSharingPublisherId": 55555,
    "commissionSharingSelectedRatePublisherId": 189069,
    "campaign": "campaign name"
    "siteName": "Publisher",
    "commissionStatus": "pending",
    "commissionAmount": {
      "amount": 5.59,
      "currency": "GBP"
    },
    "saleAmount": {
      "amount": 55.96,
      "currency": "GBP"
    },
    "ipHash": "-66667778889991112223",
    "customerCountry": "GB",
    "clickRefs": {
      "clickRef": "12345",
      "clickRef2": "22222",
      "clickRef3": "33333",
      "clickRef4": "44444",
      "clickRef5": "55555",
      "clickRef6": "66666"
    },
    "clickDate": "2017-01-23T12:18:00",
    "transactionDate": "2017-02-20T22:04:00",
    "validationDate": null,
    "type": "Commission group transaction",
    "declineReason": null,
    "voucherCodeUsed": true,
    "voucherCode": "example123",
    "lapseTime": 2454307,
    "amended": false,
    "amendReason": null,
    "oldSaleAmount": null,
    "oldCommissionAmount": null,
    "clickDevice": "Windows",
    "transactionDevice": "Windows",
    "publisherUrl": "http://www.publisher.com/search?query=dvds",
    "advertiserCountry": "GB",
    "orderRef": "111222333444",
    "customParameters": [
      {
        "key": "1",
        "value": "555666"
      },
      {
        "key": "2",
        "value": "example entry"
      },
      {
        "key": "3",
        "value": "LLLMMMNNN"
      }
    ],
    "transactionParts": [
      {
        "commissionGroupId": 12345,
        "amount": 44.76,
        "commissionAmount": 4.50,
        "commissionGroupCode": "DEFAULT",
        "commissionGroupName": "Default Commission"
      }
 
      {
 
        "commissionGroupId": 654321,
        "amount": 11.20,
        "commissionAmount": 1.50,
        "commissionGroupCode": "EXISTING",
        "commissionGroupName": "EXISTING"
      }
 
 
    ],
    "paidToPublisher": false,
    "paymentId": 0,
    "transactionQueryId": 0,
    "originalSaleAmount": null
  },
    "basketProducts": null
}
]"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2020-12-18T00%3A00%3A00&endDate=2021-01-17T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response=[
  
]"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2021-01-17T00%3A00%3A00&endDate=2021-02-16T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response=[
  
]"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2021-02-16T00%3A00%3A00&endDate=2021-03-18T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response={
   "error":"request.limit.exceeded",
   "description":"Requests limit '20' times per '1' minutes' is exceeded"
}"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2021-03-18T00%3A00%3A00&endDate=2021-04-17T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response={
   "error":"request.limit.exceeded",
   "description":"Requests limit '20' times per '1' minutes' is exceeded"
}"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2021-04-17T00%3A00%3A00&endDate=2021-05-17T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response={
   "error":"request.limit.exceeded",
   "description":"Requests limit '20' times per '1' minutes' is exceeded"
}"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2021-05-17T00%3A00%3A00&endDate=2021-06-16T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response={
   "error":"request.limit.exceeded",
   "description":"Requests limit '20' times per '1' minutes' is exceeded"
}"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2021-06-16T00%3A00%3A00&endDate=2021-07-16T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response={
   "error":"request.limit.exceeded",
   "description":"Requests limit '20' times per '1' minutes' is exceeded"
}"<br>url=https"://api.awin.com/publishers/[PUBLISHER_ID]/transactions/?startDate=2021-07-16T00%3A00%3A00&endDate=2021-08-15T00%3A00%3A00&timezone=Europe/Berlin&dateType=transaction<br>response={
   "error":"request.limit.exceeded",
   "description":"Requests limit '20' times per '1' minutes' is exceeded"
}<br><pre>array(13){
   [
      0
   ]=>
  array(0){
      
   }[
      1
   ]=>
  array(0){
      
   }[
      2
   ]=>
  array(0){
      
   }[
      3
   ]=>
  array(0){
      
   }[
      4
   ]=>
  array(13){
      [
         0
      ]=>
    array(40){


Ab 118 Zeiel geht es dann ne ganze weile so weiter bis Zeile 2964. Der Tail davon (ab Zeile 2930 bis Ende) sieht dann so aus:

JSON:
}[
      10
   ]=>
  array(2){
      [
         "error"
      ]=>
    string(22)"request.limit.exceeded"[
         "description"
      ]=>
    string(54)"Requests limit '20' times per '1' minutes' is exceeded"
   }[
      11
   ]=>
  array(2){
      [
         "error"
      ]=>
    string(22)"request.limit.exceeded"[
         "description"
      ]=>
    string(54)"Requests limit '20' times per '1' minutes' is exceeded"
   }[
      12
   ]=>
  array(2){
      [
         "error"
      ]=>
    string(22)"request.limit.exceeded"[
         "description"
      ]=>
    string(54)"Requests limit '20' times per '1' minutes' is exceeded"
   }
}"<pre>"
 
Status
Dieses Thema wurde gelöst! Zur Lösung gehen…
Zurück