Array Wildcards?

Liebe Catmandu-Spezis,

ich wollte mal nachfragen, wie das typische Verhalten von den Array-Wildcards (*,$append, $prepend, $last ,$first) ist (Ich nutze Array und Liste im folgenden austauschbar):

Beim Lesen von Werten eines Quellfeldes:

  • $append/$prepend machen nichts
  • $first/$last/* setzen einen Array voraus, lesen dann den ersten/letzten/jeden Wert des Indes

Beim Schreiben eines neuen oder über ein bestehendes Feld:

  • $append/$prepend hängen an am Anfang bzw am Ende eines bestehenden Arrays etwas an oder erzeugen einen neuen Array, wenn dieser nicht vorhanden ist
  • $first/$last/* schreiben an der ersten/letzten oder jeden index-Position etwas

Ist das soweit richtig?
Setzten $first/$last/* beim Schreiben die Existenz eines Arrays voraus und machen nichts wenn kein passender Array existiert? Ich vermute ja
Gilt das Verhalten der Array-Wildcards für alle Fix-Funktionen?
Gilt das Verhalten sowohl für Arrays von Strings als auch Listen von Objekten?
Das Schreiben von verschachtelten Arrays wie beim Pfad z.B. sehr.$last.verschachtelter.$append.pfad, würde die Liste für sehr vorausgesetzt und für verschachtelt könnte neu erzeugt werden falls er nicht existiert.

Danke und Gruß

@nichtich @jorol @vpeil Kann jemand von euch hier helfen?

Beim Lesen von Werten eines Quellfeldes:

  • $append/$prepend machen nichts
  • $first/$last/* setzen einen Array voraus, lesen dann den ersten/letzten/jeden Wert des Indes

Ok

Beim Schreiben eines neuen oder über ein bestehendes Feld:

  • $append/$prepend hängen an am Anfang bzw am Ende eines bestehenden Arrays etwas an oder erzeugen einen neuen Array, wenn dieser nicht vorhanden ist
  • $first/$last/* schreiben an der ersten/letzten oder jeden index-Position etwas

Ok

Ist das soweit richtig?

Ja, mit der Einschränkung, dass du mit * keine Liste erzeugen kannst:

$ echo '{}' | catmandu convert JSON to YAML --fix 'add_field(list.*,a)'
--- {}
...

Setzten $first/$last/* beim Schreiben die Existenz eines Arrays voraus und machen nichts wenn kein passender Array existiert? Ich vermute ja

Nein, Catmandu erzeugt ggf. die notwendige Datenstruktur:

echo '{}' | catmandu convert JSON to YAML --fix 'add_field(list.$last,a)'
---
list:
- a
...

Gilt das Verhalten der Array-Wildcards für alle Fix-Funktionen?

Wenn die Fixes paths unterstützen, sollte das funktionieren.

Gilt das Verhalten sowohl für Arrays von Strings als auch Listen von Objekten?

Ja, z.B.:

echo '{"list":[[1,2,3],{"foo":"bar"},"baz"]}' | catmandu convert JSON to YAML --fix 'set_field(list.*,a)'
---
list:
- a
- a
- a
...

Das Schreiben von verschachtelten Arrays wie beim Pfad z.B. sehr.$last.verschachtelter.$append.pfad, würde die Liste für sehr vorausgesetzt und für verschachtelt könnte neu erzeugt werden falls er nicht existiert.

Kommt auf den Fix an, add_field erzeugt z.B. eine verschachtelte Datenstruktur

$ echo '{}' | catmandu convert JSON to YAML --fix 'add_field(sehr.$last.verschachtelter.$append.pfad,a)'
---
sehr:
- verschachtelter:
  - pfad: a
...

set_field setzt hingegen eine definierte/vorhandene Datenstruktur voraus:

$ echo '{}' | catmandu convert JSON to YAML --fix 'set_field(sehr.$last.verschachtelter.$append.pfad,a)'
--- {}
...
2 Likes

Danke für die super ausführliche Antwort. Das hilft mir und uns im hbz sehr. Besonders auch, wie du das mithilfe des Bash-Einzeilers Catmandu-Fixes testest.

Wir passen aktuell nochmal im Kontext von Metafacture Fix noch ein paar Sachen an und wollen hier nochmal näher an Catmandu heran.

Ich hätte noch Rückfragen, wenn ich darf:

  • die „Familie“ der set_-Fixes sind die einzigen Funktionen, die eine bestehende Datenstruktur voraussetzen? Richtig?

  • Bei set_array bekomme ich es aktuell noch nicht hin, einen Array in einer verschachtelten Ebene zu schreiben. Wie erzeuge ich einen Array auf einer verschachtelten unteren Ebene?

$ echo '{}' | catmandu convert JSON to YAML --fix 'set_array(list) set_array(list.$append.a)'
---
list: []
...

Erwartet hätte ich:

---
list:
 - a: []
...
  • Ist die Fehlermeldung bei set_field beabsichtigt:
$ echo '{}' | catmandu convert JSON to YAML --fix 'set_array(list) set_field(list.$last,a)'
Oops! One of your fixes threw an error...
Error: Modification of non-creatable array value attempted, subscript -1 at (eval 406) line 1.
Input:
$VAR1 = {
          'list' => []
        };
  • Wie macht ihr das in der Praxis mit copy_field und paste, wenn ihr nur in eine verschachtelte Listen-Struktur schreiben wollt, wenn vorher auch die Position im Array erzeugt worden ist oder existiert:
$ echo '{ "a":[{"test":"wert"} ], "b":"c" } {"b":"c" }' | catmandu convert JSON to YAML --fix 'copy_field(b,a.$last.d)'
---
a:
- d: c
  test: wert
b: c
...
---
a:
- d: c
b: c
...

Als Ergebnis hätte ich gerne

---
a:
- d: c
  test: wert
b: c
...
---
b: c
...

Vielen Dank!

die „Familie“ der set_-Fixes sind die einzigen Funktionen, die eine bestehende Datenstruktur voraussetzen? Richtig?

Soweit ich das überblicke, ja.

Bei set_array bekomme ich es aktuell noch nicht hin, einen Array in einer verschachtelten Ebene zu schreiben. Wie erzeuge ich einen Array auf einer verschachtelten unteren Ebene?
$ echo ‚{}‘ | catmandu convert JSON to YAML --fix ‚set_array(list) set_array(list.$append.a)‘

Dein Beispiel soll einen hash in einem array erzeugen, so funktioniert es:

$ echo '{}' | catmandu convert JSON to YAML --fix 'set_array(list,1,2,3);set_hash(list.$append,a:4)'
---
list:
- '1'
- '2'
- '3'
- a: '4'

Einen array of arrays erzeugst Du so;

$  echo '{}' | catmandu convert JSON to YAML --fix 'set_array(list,1,2,3);set_array(list.$append,4,5,6)'
---
list:
- '1'
- '2'
- '3'
- - '4'
  - '5'
  - '6'
...

Ist die Fehlermeldung bei set_field beabsichtigt:
$ echo ‚{}‘ | catmandu convert JSON to YAML --fix ‚set_array(list) set_field(list.$last,a)‘
Oops! One of your fixes threw an error…

Ja, set_ setzt eine bestehende Datenstruktur voraus, verwende add_field um eine neue zu erzeugen:

$ echo '{}' | catmandu convert JSON to YAML --fix 'set_array(list);add_field(list.$last,a)'
---
list:
- a
...

Wie macht ihr das in der Praxis mit copy_field und paste, wenn ihr nur in eine verschachtelte Listen-Struktur schreiben wollt, wenn vorher auch die Position im Array erzeugt worden ist oder existiert:
$ echo ‚{ „a“:[{„test“:„wert“} ], „b“:„c“ } {„b“:„c“ }‘ | catmandu convert JSON to YAML --fix ‚copy_field(b,a.$last.d)‘

Du übergibst hier zwei JSON-Objekte an Catmandu, die beide einen Schlüssel „b“ enthalten, die nacheinander verarbeitet werden und auf die der Fix copy_field angewendet wird, insofern ist das Ergebnis erwartungskonform.

1 Like

Danke nochmal :slight_smile: Noch Nachfragen zwei Dingen, dann hab ich alles :slight_smile: :

  1. Schreiben eines arrays in ein array of objects

Hier war ich etwas ungenau: Ich möchte weder „nur“ ein hash mit einfachen Feldern erzeugen, noch einen affary of arrays erzeugen.

Ich würde stattdessen gerne in einen neuen Array of Objects mit dem Namen list, ein neues Listenobjekt schreiben/appenden, das ein leeren Array mit dem Namen a beinhaltet(list.1.a[]): neuer array wird erzeugt mit set_array(list) Objekt mit beinhaltendem Array in list appenden (set_array(list.$append.a)

Die Struktur list besteht ja, daher hätte ich erwartet, dass ich einen array a mit set_array(list.$append.a ergänzen kann.

Entsprechend mein erwartetes Ergebnis:

---
list: <- arrayOfObjects
 - a: [] <- neue leere Liste mit dem Namen `a` auf Pfad list.1.a (nicht einfach nur ein hash)
...

Ich habe gerade nochmal rum getestet. Folgendes klappt:

$ echo '{}' | catmandu convert JSON to YAML --fix 'set_array(list) set_hash(list.$append)  set_array(list.$last.a)'
---
list:
- a: []
...

Muss ich dann immer ein set_hash(list.$append) vorher setzen, da für set_array(list.$append.a) keine ausreichende Struktur vorhanden ist ?


  1. Best practice für ein Vermeiden des Kopierens/Pastens bei $last/$fist, wenn Array bisher leer ist oder nicht existiert.

Das Ergebnis ist erwartungskonform, mir geht es hier eher darum, wie ihr das in der Praxis macht, welche fix ihr gebrauchen würden, damit das Feld b NUR dann in den Array kopiert wird, wenn bereits das Array gefüllt ist oder existiert. Also nur dann, wenn zumindest list.1 existiert.

$ echo '{ "a":[{"test":"wert"} ], "b":"c" } { "a":[ ], "b":"c" } {"b":"c" }' | catmandu convert JSON to YAML --fix 'copy_field(b,a.$last.d)'
---
a:
- d: c
  test: wert
b: c
...
---
a:
- d: c
b: c
...
---
a:
- d: c
b: c
...
"a":[{"test":"wert"} ], "b":"c" } <-- hier sollte das kopieren von b erfolgen
{ "a":[ ], "b":"c" } <-- hier nicht, weil Liste a leer
{"b":"c" } <-- hier nicht, weil Liste a nicht existent
---
a:
- d: c
  test: wert
b: c
...
---
a: []
b: c
...
---
b: c
...

Danke :slight_smile:

Der Pfad list.$append.a referenziert einen key eines objects in einem array der noch nicht definiert ist.

Du könntest einen array of objects erzeugen, deren keys undefinierte values haben und im Anschluss die values mit arrays besetzen:

$ echo '{}' | catmandu convert JSON to YAML --fix 'set_array(list);set_hash(list.$append,a:"");set_hash(list.$append,b:"");set_array(list.0.a,1,2);set_array(list.1.b,3,4)'
---
list:
- a:
  - '1'
  - '2'
- b:
  - '3'
  - '4'
...

Ja.

Du musst eine Fix::Condition verwenden:

$  echo '{ "a":[{"test":"wert"} ], "b":"c" } { "a":[ ], "b":"c" } {"b":"c" }' | catmandu convert JSON to YAML --fix 'if exists(a.0) copy_field(b,a.$last.d) end'
---
a:
- d: c
  test: wert
b: c
...
---
a: []
b: c
...
---
b: c
...

Grüße

1 Like

Danke!!!