Dieser Artikel ist der 7. Tagesartikel von WACUL Adventskalender 2016. Ich arbeite seit September dieses Jahres bei WACUL und schreibe hauptsächlich Go-Code und manchmal Python im Backend.
Übrigens wurde es länger, während ich dies und das schrieb. Wenn Sie also nur wissen möchten, was Sie getan haben Einführung Nach dem Lesen von% 81% 98% E3% 82% 81% E3% 81% AB) [Endlich das Hauptthema](http://qiita.com/podhmo/items/a12952b07648e42911b4#%E3%82%88%E3 Bitte überspringen Sie zu ungefähr% 81% 86% E3% 82% 84% E3% 81% 8F% E6% 9C% AC% E9% A1% 8C).
Vor kurzem schreibe ich jeden Tag Go-Code in meinem Geschäft. Gelegentlich wollte ich Go-Code in Python schreiben. Du denkst vielleicht, dass es seltsam ist. Aber bitte warten Sie. Derzeit ist es das Ergebnis der Verfolgung des folgenden Gedankenflusses.
Natürlich ist es sicherlich besser, die obigen Schritte so weit wie möglich auszuführen.
Der Unterschied zwischen * 1. * und * 2. * ist subjektiv und möglicherweise schwer zu verstehen.
Was * 3. * betrifft, fühle ich mich glücklich. Wenn nicht, kann es gut sein, es mit go zu machen.
Trotzdem denke ich, dass es Zeiten gibt, in denen es sich um eine interessenorientierte Suche handelt oder wenn Sie ein wenig in einer vertrauten Sprache arbeiten. Es kann schneller sein, schnell etwas zu erstellen, das sich nach Ihrem persönlichen Geschmack verhält, als sich zu bemühen, etwas für allgemeine Zwecke zu schaffen. Persönlich war ich mit Python vertraut, also habe ich mich für Python entschieden.
Wenn Sie an die Go-Code-Generierung denken, denken Sie normalerweise an die folgenden zwei Dinge.
Diesmal ist es eine andere Geschichte.
prestring?
Eigentlich habe ich meine eigene Bibliothek für Arbeiten wie die Codegenerierung verwendet. Es ist eine Bibliothek namens prestring. Ich glaube nicht, dass es jemand weiß, deshalb werde ich ein wenig über diese Bibliothek erklären.
Diese Bibliothek ist keine (transpile) Bibliothek, die Go-Code aus Python oder DSL generiert, sondern nur eine Bibliothek zum direkten Handschreiben des Codes einer bestimmten Sprache Y (hier go) in Python. Dies entspricht der Verwendung einer Vorlagen-Engine für die automatische Codegenerierung. Das Gefühl, die volle Sprachfunktion zu nutzen, kann unterschiedlich sein.
Als Feature habe ich den Bereich hervorgehoben, der das Verwalten von Einrückungen erleichtert. Die ursprüngliche Idee selbstD Sprache für den Sieg|Programmierung| POSTDErscheintinArtikelnetc.srcgenDie Bibliothek ist abgeleitet von.
Der Prestring wird von einer Klasse namens "Module" für jedes Modul bereitgestellt. Der Code wird generiert, wenn das Ergebnis der Ausführung verschiedener Operationen für das Objekt dieser Klasse ausgegeben wird. Übrigens, obwohl es der Name Prestring ist, erinnere ich mich, dass es den Zustand vor der Zeichenkette bedeutete.
m = Module()
m.stmt("hai")
print(m) # => "hai\n"
Schreiben wir Hallo Welt als einfachen Code. Hallo Welt sieht so aus:
from prestring.go import Module
m = Module()
m.package("main")
with m.import_group() as im:
im.import_("fmt")
with m.func("main"):
m.stmt("fmt.Println(`hello world`)")
print(m)
Verwenden Sie als Verwendung die Syntax with, wenn Sie einen Einzug wünschen. Sie können es mit dem Gefühl schreiben, eine Methode mit einem Namen zu verwenden, der dem reservierten Wort jeder Sprache ähnelt. Sobald Sie sich daran gewöhnt haben, können Sie die Ausgabesprache so sehen, wie sie ist. Wahrscheinlich. sicherlich. vielleicht.
Tatsächlich gehen die obigen Code-Ausgaben wie folgt aus: Selbst wenn die Ausgabe etwas klobig ist, ist dies praktisch, da gofmt
sie formatiert.
package main
import (
"fmt"
)
func main() {
fmt.Println(`hello world`)
}
Lassen Sie uns automatisch einen etwas komplizierteren Code generieren. Ich möchte einen Code schreiben, der das direkte Produkt einer Liste und einer Liste berechnet. Zum Beispiel ist das direkte Produkt der beiden Listen xs und ys wie folgt.
xs = [1, 2, 3]
ys = ["a", "b", "c"]
[(x, y) for x in xs for y in ys]
# => [(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c'), (3, 'a'), (3, 'b'), (3, 'c')]
Betrachten Sie in ähnlicher Weise zwei Fälle, drei Fälle, ... N Fälle und so weiter. Normalerweise würden Sie Code beliebig oft mit rekursivem usw. schreiben. Dieses Mal möchte ich einen Code schreiben, der den Code für jeden Fall ausgibt.
Schreiben wir zunächst den Code, der den Go-Code für die beiden Fälle direkt generiert. Es wird wie folgt aussehen.
from prestring.go import GoModule #Gleich wie Modul
# cross product
def cross2(m):
with m.func("cross2", "vs0 []string", "vs1 []string", return_="[][]string"):
m.stmt("var r [][]string")
with m.for_("_, v0 := range vs0"):
with m.for_("_, v1 := range vs1"):
m.stmt("r = append(r, []string{v0, v1})")
m.return_("r")
return m
m = GoModule()
m.package("main")
print(cross2(m))
Wenn mit angehängt ist, können Sie es irgendwie lesen, wenn Sie bedenken, dass es sich um einen Einzug handelt. Das Ausgabeergebnis ist wie folgt.
func cross2(vs0 []string, vs1 []string) [][]string {
var r [][]string
for _, v0 := range vs0 {
for _, v1 := range vs1 {
r = append(r, []string{v0, v1})
}
}
return r
}
Betrachten Sie nun den Fall, in dem die Anzahl der übergebenen Listen 3, 4, ... und eine beliebige Anzahl der übergebenen Listen beträgt. Es kann etwas ärgerlich sein zu sehen, wie viele Schleifennester sich abhängig von der Anzahl der übergebenen Listen ändern. In einem solchen Fall können Sie es nicht direkt so schreiben, wie es ist. Wenn Sie es jedoch rekursiv schreiben, können Sie Schleifencode generieren, der jederzeit verschachtelt ist, während die Einrückungsstruktur beibehalten wird.
def crossN(m, n):
def rec(m, i, value):
if i >= n:
m.stmt("r = append(r, []string{{{value}}})".format(value=", ".join(value)))
else:
v = "v{}".format(i)
vs = "vs{}".format(i)
with m.for_("_, {} := range {}".format(v, vs)):
value.append(v)
rec(m, i + 1, value)
args = ["vs{} []string".format(i) for i in range(n)]
with m.func("cross{}".format(n), *args, return_="[][]string"):
m.stmt("var r [][]string")
rec(m, 0, [])
m.return_("r")
return m
Es ist etwas schwer zu lesen. Wenn man bedenkt, dass das innere mit geschrieben wird, bevor das äußere mit beendet wird, und der Aufruf verschachtelt ist, sieht die Struktur wie folgt aus. Gewöhnen Sie sich also bitte daran.
Bühnenschleife mit v0
Bühnenschleife mit v1
Schleife mit v2 Bühne
...
Schleife mit vN-Stufe
Wenn Sie versuchen, das Ergebnis von "crossN (m, 5)" auszugeben, wenn N = 5 ist, ist es wie folgt.
package main
func cross5(vs0 []string, vs1 []string, vs2 []string, vs3 []string, vs4 []string) [][]string {
var r [][]string
for _, v0 := range vs0 {
for _, v1 := range vs1 {
for _, v2 := range vs2 {
for _, v3 := range vs3 {
for _, v4 := range vs4 {
r = append(r, []string{v0, v1, v2, v3, v4})
}
}
}
}
}
return r
}
Es gab noch eine weitere Besonderheit. Es ist eine Funktion namens Submodul. Diese Funktion ist eine Funktion, mit der Sie einer bestimmten Position in einer bestimmten Ausgabe einen Marker hinzufügen und den Zeichenfolgenausdruck einbetten können, den Sie später an dieser Position einfügen möchten. Es sieht wie folgt aus.
m = Module()
m.stmt("begin foo")
sm = m.submodule()
m.stmt("end foo")
m.stmt("bar")
sm.sep()
sm.stmt("** yay! **")
sm.sep()
print(m)
Erstellen Sie ein Submodul namens sm an der Position, die von foo von m umgeben ist. Ich werde später einen Zeilenumbruch und einige Formulierungen einbetten. Das Ausgabeergebnis ist wie folgt.
begin foo
** yay! **
end foo
bar
Diese Funktion wird im Importteil verwendet. So können Sie den Import des Pakets schreiben, das Sie später benötigen.
from prestring.go import Module
m = Module()
m.package('main')
with m.import_group() as im:
pass
with m.func('main'):
im.import_('log')
m.stmt('log.Println("hmm")')
print(m)
"Log" wird zum ersten Mal in die Hauptfunktion importiert. Wenn Sie das Ausgabeergebnis betrachten, wird es an der durch "import_group ()" angegebenen Position eingefügt.
package main
import (
"log"
)
func main() {
log.Println("hmm")
}
Es ist jedoch eine gute Idee, "goimports" anstelle von "gofmt" zu verwenden, um den Importteil automatisch einzufügen, sodass er in go möglicherweise nicht häufig verwendet wird.
Code generieren Jetzt, da ich die Möglichkeit habe, Code zu generieren, hatte ich manchmal das Gefühl, alles mit der Codegenerierung zu tun. Zusammenfassend kann ich es nicht sehr empfehlen. Es ist am besten, das zu tun, was Sie zur Laufzeit berechnen können (ohne Reflektion zu verwenden).
Zum Beispiel fizzbuzz geschrieben von * ernsthafter Person * Ich bin nicht so glücklich, das zu tun. Weil ich es kann Die Unterscheidung zwischen gut und schlecht basiert auf meiner eigenen Faulheit. Bitte urteilen.
Persönlich finde ich die folgenden Kriterien gut.
Das letzte ist eine leicht generische Version des Generationslückenmusters (das ich kürzlich gelernt habe).
Außerdem wird der Versuch, die fehlenden Sprachfunktionen auszugleichen, wahrscheinlich fehlschlagen.
Am Beispiel von Generika ist es möglich, eine einphasige "TContainer" -ähnliche Definition zu generieren, die "Container
Es war eine lange Zeit, aber es ist endlich das Hauptthema. Es ist ungefähr auf halbem Weg hier.
JSON-to-GO?
Es scheint, dass Qiita den folgenden Artikel hatte.
Ich habe den obigen Artikel gelesen und herausgefunden. Es scheint einen praktischen Dienst namens JSON-to-Go zu geben. Dies bedeutet, dass die entsprechende Go-Typ-Definition ausgegeben wird, wenn Sie den JSON der API-Antwort übergeben.
Sie können sehen, wie es aussieht, indem Sie auf den Beispiellink in der GitHub-API klicken.
Übrigens scheint die in diesem Service verwendete Implementierung des Konvertierungsteils auf Github zu sein. Es war Code in js geschrieben.
Es ist gut, dies so zu verwenden, wie es ist. Es ist ein gutes Thema, also werde ich versuchen, es auf Python zu portieren. Es ist jedoch ein Versuch, die oben erwähnte Bibliothek namens Prestring zu verwenden, um etwas zu erstellen, das ungefähr die gleiche Ausgabe und nicht einen vollständigen Port erzeugt.
Der portierte Code sieht wie folgt aus.
Es sind ungefähr 120 Zeilen, also ist es nicht so harter Code. Der ursprüngliche js-Code ist mit etwas mehr als 230 Zeilen auch etwas mehr als der Python-Code, aber nicht so groß. Der Grund für den Unterschied in der Codegröße ist, dass die ursprüngliche js-Implementierung den Prozess der Konvertierung in einen Namen für unterwegs definiert. Das ist reduziert, also sind es ungefähr 120 Zeilen. (Übrigens schien der Prozess der Konvertierung in den Namen für go, der im ursprünglichen json-to-go verwendet wurde, praktisch zu sein. Es gibt eine Geschichte, in der dasselbe im Prestring aufgenommen wurde.)
Nun, ich würde gerne die Portierungsarbeit erledigen, aber wenn ich die folgenden Operationen ausführen kann, kann ich anscheinend von JSON in Go-Code konvertieren.
Schauen Sie sich für jeden den Originalcode von json-to-go.js an und schreiben Sie, was Sie interessant finden.
Einige der besseren Namenskonvertierungen für go sind:
>>> from prestring.go import goname
>>> goname("foo_id")
'FooID'
>>> goname("404notfund")
'Num404Notfund'
>>> goname("1time")
'OneTime'
>>> goname("2times")
'TwoTimes'
>>> goname("no1")
'No1'
Konvertierungen wie das Entfernen von Unterstrichen oder das Hinzufügen eines speziellen Präfixes zu Namen, die mit einer Zahl beginnen. So etwas wie Camel Case.
Beispielsweise scheinen URL, API, ASCII, ... speziell für ein bestimmtes Akronym verarbeitet zu werden. Ich gebe mein Bestes. Eine Liste dieser speziell behandelten Akronyme aus dem Golint-Code entnommen.
Grundsätzlich scheint der Typ eins zu eins mit einer if-Anweisung aus dem Wert des Ergebnisses der Deserialisierung von JSON verzweigt zu sein.
Ich dachte, es wären die folgenden drei Dinge.
Dies kann so wie es ist ordentlich geschrieben werden. Ich meine, ich habe den ursprünglichen js-Code nicht ernsthaft gelesen. Es ist kurz, also werde ich es hier posten. Es wird durch das folgende Verfahren erzeugt.
Laden Sie für die JSON-Last von "1" einfach den übergebenen JSON. Als Vorprozess werde ich einen Prozess einschließen, um das obige ".0" in ".1" zu konvertieren (wahrscheinlich nicht erforderlich in Python, aber es scheint nur für den Fall zu sein).
Es ist die Erzeugung von Strukturinformationen von 2.
. struct info ist keine Übertreibung, es ist das folgende Wörterbuch. Es ist ein Bild, das den gesamten JSON einmal analysiert und die folgenden Informationen verwendet.
{"freq": 1, "type": "int", "children": {}, "jsonname": "num"}
freq ist die Häufigkeit des Auftretens, type ist der Typ in go, Kinder enthalten untergeordnete Elemente, wenn es sich um ein Wörterbuch (struct) handelt, und jsonname ist der ursprüngliche json-Name (der Schlüssel des Wörterbuchs ist der Typname von go).
Dies ist der Codegenerierungsteil von 3.
. Dies ist der Teil, in dem ich so schön schreiben kann, wie es ist. Ist es nicht eine Codeebene, die so gelesen werden kann, wie sie ist?
def emit_code(sinfo, name, m):
def _emit_code(sinfo, name, m, parent=None):
if sinfo.get("type") == "struct":
with m.block("{} struct".format(name)):
for name, subinfo in sorted(sinfo["children"].items()):
_emit_code(subinfo, name, m, parent=sinfo)
else:
m.stmt('{} {}'.format(name, to_type_struct_info(sinfo)))
# append tag
if is_omitempty_struct_info(sinfo, parent):
m.insert_after(' `json:"{},omitempty"`'.format(sinfo["jsonname"]))
else:
m.insert_after(' `json:"{}"`'.format(sinfo["jsonname"]))
with m.type_(name, to_type_struct_info(sinfo)):
for name, subinfo in sorted(sinfo["children"].items()):
_emit_code(subinfo, name, m, parent=sinfo)
return m
Deshalb habe ich einen JSON für den Strukturkonverter.
Unter Verwendung von portiertem Code [github API-Antwort](https: // github). Das Ergebnis der Konvertierung von com / podhmo / advent2016 / blob / master / json / github.json) ist so sieht es aus .gehen). Da es sich um eine lange Ausgabe handelt, habe ich einen separaten Link erstellt.
Der JSON-to-Go-Konverter, den ich bekommen habe, also ändern wir ihn ein wenig.
Danach werde ich das Ausgabeergebnis weiterhin mit der JSON-API-Antwort von github überprüfen, die auch in JSON-to-Go verwendet wurde.
Möglicherweise möchten Sie einem anderen Typ eine bestimmte Zeichenfolge zuweisen, z. B. eine URI oder eine E-Mail-Adresse. Ändern Sie beispielsweise die Verwendung von Uri von strfmt.
Versuchen Sie es mit strfmt.Uri, wenn ": //" enthalten ist. Ich habe auch versucht, Import hinzuzufügen, wenn strfmt.Uri verwendet wird. Ändern Sie einige Zeilen.
Wenn Sie sich ernsthaft mit verschiedenen Typen befassen möchten, werden Sie anscheinend die Korrespondenz in dem Teil schreiben, der die Art des Go errät.
Der folgende Ausgabeteil
package autogen
type AutoGenerated struct {
CloneURL string `json:"clone_url"`
CreatedAt time.Time `json:"created_at"`
...
Es wurde wie folgt.
package autogen
import (
"github.com/go-openapi/strfmt"
"time"
)
type AutoGenerated struct {
CloneURL strfmt.Uri `json:"clone_url"`
CreatedAt time.Time `json:"created_at"`
...
Von hier an beginnt das Zögern. Die Änderungen, die ich für möglich hielt, wenn ich mich ein wenig um ein ungezwungenes Gefühl kümmerte, verursachten verschiedene problematische Probleme.
[Verknüpfter Artikel](http://qiita.com/minagoro0522/items/dc524e38073ed8e3831b#%E8%A4%87%E9%9B%91%E3%81%AA%E6%A7%8B%E9%80% A0% E3% 81% AE-json-% E5% 87% A6% E7% 90% 86% E3% 81% A7% E7% 9B% B4% E9% 9D% A2% E3% 81% 99% E3% 82 % 8B% E5% 95% 8F% E9% A1% 8C) war ziemlich dissed, aber ich habe einen Codegenerator. Ich wollte Änderungen einbeziehen, die die Ausgabeergebnisse erheblich verändern würden. Versuchen Sie, die Struktur der Ausgabestruktur von einer verschachtelten in eine Struktur mit einer flachen Struktur zu ändern. Vorerst habe ich beschlossen, den damaligen Feldnamen als Namen der Struktur zu verwenden.
Änderungen sind ungefähr 10 Zeilen.
Die folgende Definition
type AutoGenerated struct {
CloneURL strfmt.Uri `json:"clone_url"`
...
Name string `json:"name"`
OpenIssuesCount int `json:"open_issues_count"`
Organization struct {
AvatarURL strfmt.Uri `json:"avatar_url"`
EventsURL strfmt.Uri `json:"events_url"`
Es hat sich wie folgt geändert.
type AutoGenerated struct {
CloneURL strfmt.Uri `json:"clone_url"`
...
Name string `json:"name"`
OpenIssuesCount int `json:"open_issues_count"`
Organization Organization `json:"organization"`
...
type Organization struct {
AvatarURL strfmt.Uri `json:"avatar_url"`
EventsURL strfmt.Uri `json:"events_url"`
Wenn Sie sich das Ausgabeergebnis ansehen, sehen Sie natürlich die [vorherige Ausgabe](https: // github). Da die Struktur der Verschachtelungsbeziehung, die in com / podhmo / advent2016 / blob / master / dst / jsontogo / github2.go) erhalten wurde, verschwunden ist, ist die Eltern-Kind-Beziehung des Werts meines Erachtens schwer zu verstehen. Machen.
Da die Struktur der Eltern-Kind-Beziehung von Werten in der flachen Ausgabe schwer zu verstehen ist, habe ich beschlossen, am Anfang der Strukturdefinition einen Kommentar hinzuzufügen, damit die Struktur der verschachtelten Beziehung übersehen werden kann.
Die Änderung beträgt ca. 20 Zeilen. Die Änderung der ursprünglichen Ausgabefunktion emit_code ()
selbst beträgt ungefähr 2 Zeilen.
Die Kommentare, die am Anfang der Definition hinzugefügt wurden, lauten wie folgt. Jetzt können Sie die Verschachtelungsbeziehung sehen.
/* structure
AutoGenerated
Organization
Owner
Parent
Owner
Permissions
Permissions
Source
Owner
Permissions
*/
Ich habe es übrigens bemerkt, und ich bin sicher, wenn Sie ein guter Mensch sind, werden Sie es sofort bemerken. Es gibt ein Problem. Namen können Konflikte verursachen. In der Ausgabe der ursprünglich verschachtelten Struktur musste die Struktur nicht benannt werden, da es sich um eine unmittelbare Definition handelte. Da die verschachtelte Ausgabe flach ist, benötigt die Strukturdefinition jetzt einen Namen. Ich habe den Feldnamen direkt verwendet. In der folgenden JSON-Datei treten beispielsweise Konflikte mit den Namen auf.
{
"title": "Erstes Tagebuch",
"author": "foo",
"content": {
"abbrev": "Ich werde ab heute mit dem Bloggen beginnen....",
"body": "Ich werde ab heute mit dem Bloggen beginnen. Dieser Artikel ist der X. Tag des Adventskalenders.... ....."
},
"ctime": "2000-01-01T00:00:00Z",
"comments": [
{
"author": "anonymous",
"content": {
"body": "hmm"
},
"ctime": "2000-01-01T00:00:00Z"
}
]
}
Der Inhalt des Artikelteils und der Inhalt des Kommentarteils kollidieren.
/* structure
Article
Comments
Content
Content
*/
//Inhalt des Artikelteils
type Content struct {
Abbrev string `json:"abbrev"`
Body string `json:"body"`
}
//Der Inhalt des Kommentarteils
type Content struct {
Body string `json:"body"`
}
Im Moment spielt es keine Rolle, ob es klobig ist, also vermeiden Sie Namenskonflikte. Verwenden Sie ein Objekt namens prestring.NameStore. Dies ist ein wörterbuchähnliches Objekt, das, wenn Sie einen Namen in den Wert einfügen, einen Namen zurückgibt, der Konflikte bei doppelten Namen vermeidet (überschreiben Sie "NameStore.new_name ()", um einen Namen zu generieren Sie können die Regeln ändern.
>>> from prestring import NameStore
>>> ns = NameStore()
>>> ob1, ob2 = object(), object()
>>> ns[ob1] = "foo"
>>> ns[ob2] = "foo"
>>> ns[ob1]
'foo'
>>> ns[ob2]
'fooDup1'
Änderungen sind ungefähr 10 Zeilen. Im Text wird ein Wert generiert, der für die Form des Wörterbuchs für struct eindeutig zu sein scheint, und der Name wird mit diesem als Schlüssel verwaltet.
/* structure
Article
Comments
ContentDup1
Content
*/
type Content struct {
Abbrev string `json:"abbrev"`
Body string `json:"body"`
}
type ContentDup1 struct {
Body string `json:"body"`
}
CotentDup1 ist kein guter Name. Die Kollision kann vorerst vermieden werden. Es ist großartig zu sagen, dass die Pakete getrennt sind, damit Strukturen mit demselben Namen zusammen verwendet werden können. Zum Beispiel wäre es praktisch, wenn es eine Funktion gäbe, die leicht einen Namespace einführen könnte, wie beispielsweise das Ruby-Modul. Es scheint keine Funktion zu geben, die für unterwegs verwendet werden kann, also werde ich sie verlassen.
Nachdem der Namenskonflikt behoben wurde, sehen wir uns die Ausgabe noch einmal an, indem wir den JSON der Github-API übergeben.
/* structure
AutoGenerated
Organization
Owner
Parent
Owner
Permissions
Permissions
Source
Owner
Permissions
*/
Ich habe das Gefühl, dass Eltern und Quelle die gleiche Form haben. Blick hinein Es schien dieselbe Typdefinition zu sein. Tatsächlich ist die Typdefinition von Berechtigungen schon oft vorgekommen. Das Auto Generated der obersten Ebene selbst fühlt sich wie eine Obermenge wie Source an, aber ich werde es vorerst verlassen. Ich habe das Gefühl, ich möchte doppelte Definitionen zu einer kombinieren. Machen wir das.
Der geänderte Teil besteht aus ungefähr 10 Zeilen. Da das Scannen des Kommentarteils, der die verschachtelte Struktur der Struktur ausgibt, die in der vorherigen Änderung ausgegeben wurde (4. Fügen Sie die Eltern-Kind-Beziehung des Werts als Kommentar hinzu), und das Scannen bei der Ausgabe der Strukturdefinition in erster Linie unterschiedlich sind Trennt die Funktionen.
Das Ausgabeergebnis sieht aus wie Link, und ich konnte die doppelte Definition entfernen. Aber. Es gibt ein Problem. Es wird im nächsten Schritt behoben.
Es gibt noch ein Problem. Sie können dies sehen, indem Sie sich die Teile Parent und Source ansehen. Es hat die folgende Struktur und Source und Parent haben die gleiche Form.
/* structure
AutoGenerated
Parent
Owner
Permissions
Source
Owner
Permissions
*/
Die Tatsache, dass Parent und Source dieselbe Form haben, bedeutet, dass der gleiche Typ verwendet wird. Der Name dieses Typs ist nicht gut. Wenn überhaupt, ist Source immer noch ein besserer Name, aber ein Typ namens Parent wurde definiert und verwendet. traurig.
type AutoGenerated struct {
...
Parent Parent `json:"parent"`
...
Source Parent `json:"source"`
...
}
In der ursprünglichen JSON-to-GO-Struktur handelt es sich um eine anonyme Struktur, die sofort definiert wird. Sie müssen sich also keine Gedanken darüber machen. Da Sie jedoch eine flache Struktur erstellen, müssen Sie ihr einen Namen geben und diesen Namen angeben. Möglicherweise verfügen Sie nicht über die richtigen Informationen, um dies zu tun.
Ich werde versuchen, ein wenig zu widerstehen. Es ist etwas schwierig, aber ich werde später entscheiden, welcher Typname generiert werden soll. Verwenden Sie in solchen Fällen prestring.PreString.
from prestring import PreString
from prestring.go import GoModule
m = GoModule()
p = PreString("")
m.stmt("foo")
m.stmt(p)
m.stmt("bar")
p.body.append("*inner*")
print(m)
Natürlich können Sie das zuvor eingeführte Submodul verwenden. PreString ist das grundlegendste Objekt. Wenn Sie also später nur eine Zeichenfolge festlegen möchten, sollten Sie diese verwenden.
foo
*inner*
bar
Die Änderung betrug ungefähr 10 Zeilen
Es ist etwas schwer zu verstehen, aber der Modellname wird am Ende festgelegt. Insbesondere wird in der folgenden Form entschieden.
#Übergeben Sie eine Worttabelle, die als Argument abgezogen wird
name_score_map={"parent": -1, '': -10}
#Bestimmen Sie den Modellnamen vor dem Senden. Neu hier berechnet_Verwenden Sie name als Typnamen
new_name = max(candidates, key=lambda k: name_score_map.get(k.lower(), 0))
Im Ausgabeergebnis wurde die Verwendung von Quelle anstelle von Übergeordnet geändert. Die Umsetzung hier kann nach Geschmack und individuellem Geschmack entschieden werden. Beispielsweise können Sie den konvertierten Namen möglicherweise direkt angeben. In diesem Fall kann es als Bedingung für die Eingrenzung zweckmäßig sein, zum Zeitpunkt der Suche so etwas wie einen Pfad zu übergeben, einschließlich des Typs der obersten Ebene, anstatt nur dem Argument einen Namen zu geben.
Jedenfalls konnte ich den Modellnamen zum Zeitpunkt der Ausgabe zu einem besseren Namen machen.
type AutoGenerated struct {
...
Parent Source `json:"parent"`
...
Source Source `json:"source"`
...
}
Es gibt einige Tools, die Code generieren. Dies ist nur ein kleines Murren, aber der von mockgen
in golang / mock generierte Code fügt am Anfang der Definition keinen Kommentar hinzu. Dies wird von golang / lint gescholten, was mich sehr beunruhigt.
Dies ist immer eine automatische Generierung. Wenn Sie jedoch dem von golang / mock generierten Code einen Kommentar hinzufügen und ihn entsprechend gestalten, verschwinden alle, wenn die ursprüngliche Schnittstellendefinition ersetzt und neu generiert wird. .. Da es nicht geholfen werden kann, müssen wir eine spezielle Verarbeitung durchführen, z. B. das Ausschließen vom Ziel von Golint. Ich bin müde.
Fügen wir übrigens das Ergebnis dieser Codegenerierung hinzu, da am Anfang der Definition kein Kommentar steht.
Der geänderte Teil ist eine Zeile. Ich muss prestring.LazyFormat verwenden, da ich prstring.PreString verwende. Fügen Sie einfach eine Zeile hinzu. Es ist kurz vor einem Verdauungsspiel.
Fügen wir außerdem den ursprünglichen JSON-Wert als Beispiel in das Tag ein, um festzustellen, welcher Wert eingegeben wird.
Änderungen sind einige Zeilen. Ich werde es leid, also werde ich es beenden.
Es wird jetzt im folgenden Format ausgegeben.
// AutoGenerated : auto generated JSON container
type AutoGenerated struct {
CloneURL strfmt.Uri `json:"clone_url" example:"https://github.com/octocat/Hello-World.git"`
CreatedAt time.Time `json:"created_at" example:"2011-01-26T19:01:12Z"`
DefaultBranch string `json:"default_branch" example:"master"`
Description string `json:"description" example:"This your first repo!"`
...
Der endgültige Code lautet hier.
Nachfolgend sind nur der erste und der letzte Code sowie die Ausgabeergebnisse aufgeführt.
Es ist ein kleiner Umweg. Ich war eine Weile ratlos und entschied mich für eine Ausgabe im Flachformat. Dabei haben wir eine Funktion hinzugefügt, um doppelte Definitionen zu entfernen. Dieses Entfernen doppelter Definitionen hatte einen unerwarteten Nebeneffekt. Es ist wie ein kleines Extra, aber ich werde es vorstellen.
Schauen Sie sich beispielsweise den folgenden JSON an.
{
"total": 100,
"left": {
"total": 75,
"left": {
"total": 25,
"left": {
"total": 20
},
"right": {
"total": 5
}
},
"right": {
"total": 50,
"left": {
"total": 25
},
"right": {
"total": 25
}
}
},
"right": {
"total": 25,
"left": {
"total": 20,
"left": {
"total": 10
},
"right": {
"total": 10
}
},
"right": {
"total": 5
}
}
}
Dies ist etwas, das ein schöner Zweiviertelbaum ist.
Die Ausgabe davon im ursprünglich verschachtelten Format ist wie folgt. Da der Typ generiert wurde, der direkt dem übergebenen JSON-Wert entspricht, gibt es nicht nur eine nutzlose Strukturdefinition, sondern wenn sich die Struktur auch nur geringfügig ändert, kann sie nicht analysiert werden. Schlecht.
type Tree struct {
Left struct {
Left struct {
Left struct {
Total int `json:"total"`
} `json:"left"`
Right struct {
Total int `json:"total"`
} `json:"right"`
Total int `json:"total"`
} `json:"left"`
Right struct {
Left struct {
Total int `json:"total"`
} `json:"left"`
Right struct {
Total int `json:"total"`
} `json:"right"`
Total int `json:"total"`
} `json:"right"`
Total int `json:"total"`
} `json:"left"`
Right struct {
Left struct {
Left struct {
Total int `json:"total"`
} `json:"left"`
Right struct {
Total int `json:"total"`
} `json:"right"`
Total int `json:"total"`
} `json:"left"`
Right struct {
Total int `json:"total"`
} `json:"right"`
Total int `json:"total"`
} `json:"right"`
Total int `json:"total"`
}
Wenn dies jedoch in einem flachen Format ausgegeben wird, das doppelte Definitionen eliminiert, sieht es wie folgt aus (Kommentare, die die Struktur angeben, werden weggelassen, weil sie ärgerlich sind). Es ist nur eine rekursive Definition, also ist es natürlich. gut. (Wenn dies jedoch unverändert bleibt, wird der Nullwert nicht bestimmt und er wird unendlich wiederholt, sodass es sich um einen Fehler handelt. * Es ist nutzlos, es sei denn, es wird zu Tree. Ist es ärgerlich, Verweise auf alle Strukturen als Zeiger zu verwenden? Suche nach einem Referenzzyklus zur Bestimmung des Endes)
// Tree : auto generated JSON container
type Tree struct {
Left Tree `json:"left"`
Right Tree `json:"right"`
Total int `json:"total" example:"100"`
}
Es ist lange her, aber das war's.
In diesem Artikel habe ich den Python-Code geschrieben, der den Go-Code generiert. Insbesondere habe ich versucht, einen Prozess zu implementieren, der der Konvertierung von JSON-to-Go-JSON in go-Strukturdefinition in Python ähnelt. Danach überließ ich es der Stimmung der Zeit und änderte den Code und das Ausgabeergebnis, während ich über meine Gedanken murmelte.
Ein wenig von dem, was ich dabei dachte, war, dass die Neuerfindung der Räder (in diesem Fall die Neuimplementierung) nicht unerwartet schlecht sein könnte. Zunächst erhalten Sie eine Implementierung, die alles vollständig erfasst. Sie werden wissen, wo und welche Änderungen an dieser Implementierung vorgenommen werden müssen, und es scheint, als ob es Ihre ist, und Sie möchten alles optimieren. Von dort aus beginnt eine persönliche Erkundung. Wie wäre es, diesen Teil so zu ändern? Möglicherweise können Sie die Unterteilungen und Ideen der Person sehen, die die ursprüngliche Implementierung erstellt hat, während Sie kleine Änderungen vornehmen und den Unterschied zwischen dem Änderungsergebnis und dem ursprünglichen Ergebnis vergleichen. .. In diesem Beispiel ist es beispielsweise seltsam, verschachtelte Ausgaben auszuwählen.
Und diesmal wurde es automatisch generiert, ohne die Art des Go zu berücksichtigen. Das nächste Mal dachte ich, es wäre schön, wenn ich über die automatische Generierung unter Berücksichtigung der Art des Go schreiben könnte.
Übrigens ist nur für diesen Zweck keine erneute Implementierung mit Python erforderlich, und es gibt die folgenden Tools, um die Definition von go struct aus GO made JSON auszugeben.
Nachtrag:
Einige Leute erwähnten Gojson im diesjährigen Adventskalender.