The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Quiq::Json - Erzeuge JSON-Code in Perl

BASE CLASS

Quiq::Hash

SYNOPSIS

Klasse laden und Objekt instantiieren

use Quiq::Json;

my $j = Quiq::Json->new;

JSON-Objekt via object()

$json = $j->object(
    pi => 3.14159,
    str => 'Hello world!',
    bool => \'true',
    obj => $j->object(
        id => 4711,
        name => 'Wall',
        numbers => [1..5],
    ),
    min => undef,
);

erzeugt

{
    pi: 3.14159,
    str: 'Hello world!',
    bool: true,
    obj: {
        id: 4711,
        name: 'Wall',
        numbers: [1,2,3,4,5],
    },
    min: undefined,
}

Bei der Methode $j->object()

  • bleibt die Reihenfolge der Schlüssel/Wert-Paare erhalten

  • jedes Schlüssel/Wert-Paar beginnt auf einer eigenen Zeile und wird eingerückt

JSON-Datenstruktur via encode()

$json = $j->encode({
    pi => 3.14159,
    str => 'Hello world!',
    bool => \'true',
    obj => {
        id => 4711,
        name => 'Wall',
        numbers => [1..5],
    },
    min => undef,
});

erzeugt

{bool:true,min:undefined,obj:{id:4711,name:'Wall',numbers:[1,2,3,4,5]},pi:3.14159,str:'Hello world!'}

Bei der Methode $j->encode()

  • werden die Schlüssel/Wert-Paare von JSON-Objekten alphanumerisch sortiert (bei {...} ist die Reihenfolge sonst undefiniert)

  • gibt es keine Einrückung oder Leerraum nach dem :

DESCRIPTION

Die Klasse ermöglicht die präzise Erzeugung von JavaScript-Datenstrukturen aus Perl heraus. Der Fokus liegt nicht auf der Übermittlung von Daten, sondern auf der Einsetzung der Strukturen in JavaScript-Quelltexte. Insofern gehen die Möglichketen der Klasse über JSON hinaus. Die JavaScript-Klasse Chart.js arbeitet z.B. mit einer komplexen Datenstruktur. Diese Struktur aus Perl heraus dynamisch erzeugen zu können, war der Anlass für die Entwicklung dieser Klasse. Das Perl-Modul JSON ist für diese Zwecke nicht geeignet, denn z.B. kann eine JavaScript-Datenstruktur Referenzen auf (inline definierte) JavaScript-Funktionen enthalten. Dies ist mit dieser Klasse möglich. Weitere Vorteile dieser Klasse:

  • die Reihenfolge von Objektattributen bleibt erhalten

  • Werte können literal eingesetzt werden (z.B. Funktionsdefinionen, spezielle Werte wie true, false, null usw.)

  • lesbarerer Code

EXAMPLE

Ein realer Fall. Die Erzeugung einer Konfiguration für Chart.js. Wir nutzen hier die Aliase o() und c() für die Methoden object() und code().

my @dataSets;
my $title = 'Windspeed';
my $unit = 'm/s';
my $tMin = undef;
my $tMax = undef;
my $yMin = 0;
my $yMax = undef;

$json = $j->o(
    type => 'line',
    data => $j->o(
        datasets => \@dataSets,
    ),
    options => $j->o(
        maintainAspectRatio => \'false',
        title => $j->o(
            display => \'true',
            text => $title,
            fontSize => 16,
            fontStyle => 'normal',
        ),
        tooltips => $j->o(
            intersect => \'false',
            displayColors => \'false',
            backgroundColor => 'rgb(0,0,0,0.6)',
            titleMarginBottom => 2,
            callbacks => $j->o(
                label => $j->c(qq~
                    function(tooltipItem,data) {
                        var i = tooltipItem.datasetIndex;
                        var label = data.datasets[i].label || '';
                        if (label)
                            label += ': ';
                        label += tooltipItem.value + ' $unit';
                        return label;
                    }
                ~),
            ),
        ),
        legend => $j->o(
            display => \'false',
        ),
        scales => $j->o(
            xAxes => [$j->o(
                type => 'time',
                ticks => $j->o(
                    minRotation => 30,
                    maxRotation => 60,
                ),
                time => $j->o(
                    min => $tMin,
                    max => $tMax,
                    minUnit => 'second',
                    displayFormats => $j->o(
                        second => 'YYYY-MM-DD HH:mm:ss',
                        minute => 'YYYY-MM-DD HH:mm',
                        hour => 'YYYY-MM-DD HH',
                        day => 'YYYY-MM-DD',
                        week => 'YYYY-MM-DD',
                        month => 'YYYY-MM',
                        quarter => 'YYYY [Q]Q',
                        year => 'YYYY',
                    ),
                    tooltipFormat => 'YYYY-MM-DD HH:mm:ss',
                ),
            )],
            yAxes => [$j->o(
                ticks => $j->o(
                    min => $yMin,
                    max => $yMax,
                ),
                scaleLabel => $j->o(
                    display => \'true',
                    labelString => $unit,
                ),
            )],
        ),
    ),
);

erzeugt

{
    type: 'line',
    data: {
        datasets: [],
    },
    options: {
        maintainAspectRatio: false,
        title: {
            display: true,
            text: 'Windspeed',
            fontSize: 16,
            fontStyle: 'normal',
        },
        tooltips: {
            intersect: false,
            displayColors: false,
            backgroundColor: 'rgb(0,0,0,0.6)',
            titleMarginBottom: 2,
            callbacks: {
                label: function(tooltipItem,data) {
                    var i = tooltipItem.datasetIndex;
                    var label = data.datasets[i].label || '';
                    if (label)
                        label += ': ';
                    label += tooltipItem.value + ' m/s';
                    return label;
                },
            },
        },
        legend: {
            display: false,
        },
        scales: {
            xAxes: [{
                type: 'time',
                ticks: {
                    minRotation: 30,
                    maxRotation: 60,
                },
                time: {
                    min: undefined,
                    max: undefined,
                    minUnit: 'second',
                    displayFormats: {
                        second: 'YYYY-MM-DD HH:mm:ss',
                        minute: 'YYYY-MM-DD HH:mm',
                        hour: 'YYYY-MM-DD HH',
                        day: 'YYYY-MM-DD',
                        week: 'YYYY-MM-DD',
                        month: 'YYYY-MM',
                        quarter: 'YYYY [Q]Q',
                        year: 'YYYY',
                    },
                    tooltipFormat: 'YYYY-MM-DD HH:mm:ss',
                },
            }],
            yAxes: [{
                ticks: {
                    min: 0,
                    max: undefined,
                },
                scaleLabel: {
                    display: true,
                    labelString: 'm/s',
                },
            }],
        },
    },
}

METHODS

Instantiierung

new() - Konstruktor

Synopsis

$j = $class->new(@keyVal);

Attributes

indent => $n (Default: 4)

Tiefe der Einrückung.

Returns

Objekt

Description

Instantiiere ein Objekt der Klasse und liefere eine Referenz auf dieses Objekt zurück.

Objektmethoden

code() - Erzeuge Sourcecode für JSON-Datenstruktur

Synopsis

$code = $j->code($text);    # Scalar-Kontext
($codeS) = $j->code($text); # List-Kontext

Alias

c()

Arguments

$text

Sourcecode, typischerweise mehrzeiliger JavaScript-Code.

Returns

Sourecode (String). Im List-Kontext eine Referenz auf den Code.

Description

Erzeuge Sourcecode, der in eine JSON-Datenstruktur eingebettet werden kann, und liefere diesen zurück. Der Code

  • erhält die richtige Einrückung

  • wird "as is" eingebettet, also nicht gequotet

Example

Weise Funktionsreferenz an Objekt-Attribut zu

$unit = 'm/s';
$json = $j->object(
    label => $j->code(qq~
        function(tooltipItem,data) {
            var i = tooltipItem.datasetIndex;
            var label = data.datasets[i].label || '';
            if (label)
                label += ': ';
            label += tooltipItem.value + ' $unit';
            return label;
        }
    ~),
),

liefert

{
    label: function(tooltipItem,data) {
        var i = tooltipItem.datasetIndex;
        var label = data.datasets[i].label || '';
        if (label)
            label += ': ';
        label += tooltipItem.value + ' m/s';
        return label;
    },
}

encode() - Wandele Perl- in JavaScript-Datenstruktur

Synopsis

$json = $j->encode($scalar);

Arguments

$scalar

Skalarer Wert: undef, \0, \1, Number, String, String-Referenz, Array-Referenz, Hash-Referenz.

Returns

JSON-Code (String)

Description

Wandele $scalar nach JSON und liefere den resultierenden Code zurück. Die Übersetzung erfolgt (rekursiv) nach folgenden Regeln:

undef

Wird abgebildet auf: undefined. In einem Objekt wird das betreffende Attribut weggelassen.

\1 oder \'true'

Wird abgebildet auf: true

\0 oder \'false'

Wird abgebildet auf: false

NUMBER

Wird unverändert übernommen: NUMBER

STRING

Wird abgebildet auf: 'STRING'

STRING_REF

Wird abgebildet auf: STRING (literale Einsetzung von STRING)

Dies ist z.B. nützlich, wenn ein Teil der Datenstruktur abweichend formatiert werden soll.

ARRAY_REF

Wird abgebildet auf: [ELEMENT1,ELEMENT2,...]

HASH_REF

Wird abgebildet auf: {KEY1:VALUE1,KEY2:VALUE2,...}. Im Falle des Werts undef, wird das betreffende Schlüssel/Wert-Paar weggelassen.

object() - Erzeuge Code für JSON-Objekt

Synopsis

$json = $j->object(@opt,@keyVal);    # Scalar-Kontext
($jsonS) = $j->object(@opt,@keyVal); # List-Kontext

Alias

o()

Arguments

@keyVal

Liste der Schlüssel/Wert-Paare

Options

-indent => $bool (Default: 1)

Rücke die Elemente des Hash ein.

Returns

JSON-Code (String). Im List-Kontext eine Referenz auf den Code.

Description

Erzeuge den Code für ein JSON-Objekt mit den Attribut/Wert-Paaren @keyVal und liefere diesen zurück.

Hilfsmethoden

key() - Schlüssel eines JSON-Objekts

Synopsis

$str = $j->key($key);

Arguments

$key

Schlüssel.

Returns

String

Description

Erzeuge den Code für den Schlüssel $key eines JSON-Objekts und liefere diesen zurück. Enthält der Schlüssel nur Zeichen, die in einem JavaScript-Bezeichner vorkommen dürfen, wird er unverändert geliefert, ansonsten wird er in einfache Anführungsstriche eingefasst.

Example

Schlüssel aus dem Zeichenvorrat eines JavaScript-Bezeichners:

$str = $j->Quiq::Json('borderWidth');
==>
"borderWidth"

Schlüssel mit Zeichen, die nicht in einem JavaScript-Bezeichner vorkommen:

$str = $j->Quiq::Json('border-width');
==>
"'border-width'"

VERSION

1.220

AUTHOR

Frank Seitz, http://fseitz.de/

COPYRIGHT

Copyright (C) 2024 Frank Seitz

LICENSE

This code is free software; you can redistribute it and/or modify it under the same terms as Perl itself.