Schreiben wir Python-Code, der Go-Code analysiert und Go-Code generiert

Dieser Artikel ist der 14. Tagesartikel von WACUL Adventskalender 2016. Die Person, die schreibt, arbeitet seit September dieses Jahres bei WACUL. Sie schreibt hauptsächlich Go-Code im Backend und manchmal Python.

Dieser Artikel ist eine Fortsetzung des vorherigen Artikels (http://qiita.com/podhmo/items/a12952b07648e42911b4) (der vorherige Artikel ist nutzlos lang, sodass Sie ihn nicht zum Lesen zwingen müssen).

Letztes Mal habe ich Python-Code geschrieben, der aus dem API-Antwortbeispiel Go-Code generiert, indem er JSON-to-Go imitiert. .. Dieses Mal möchte ich Go-Code anhand des Ergebnisses der Analyse von Go-Code generieren.

Einführung

Automatische Generierung des Konvertierungsprozesses

Wenn Sie beispielsweise go-swagger verwenden, wird eine Strukturdefinition für die Antwort generiert. Dies ist eine Struktur mit einer ähnlichen Struktur, die sich jedoch geringfügig von der in der internen Datenbank gespeicherten Struktur unterscheidet.

Beim Erstellen der eigentlichen Web-API muss möglicherweise die in der Datenbank persistierte Struktur für diese Antwort in die Struktur konvertiert werden. Das ist ziemlich langweilig und langweilig. Ich möchte den Code für diesen Teil automatisch generieren.

Überblick

Um die Geschichte ein wenig zusammenzufassen, möchte ich den Konvertierungscode vom Modell (src) zum Antwortobjekt (dst). Um dies zu erhalten, analysieren Sie die Strukturdefinition der Konvertierungsquelle und des Konvertierungsziels und extrahieren Sie die Typinformationen. In diesem Bereich wird go mit einem Paket usw. hergestellt. Nach dem Extrahieren der Typinformationen mit dem erstellten Tool werden diese als JSON ausgegeben. Es ist ein Versuch, mit diesem JSON einen Konvertierungscode von src nach dst zu generieren.

sample: github api

Ich möchte ein Beispiel für die Ausführung der Codegenerierung anhand eines Textbeispiels vorstellen.

Ursprünglich ist es eine Geschichte, einen Prozess zum Konvertieren des Ausdrucks der Persistenzschicht in den Ausdruck der Antwort zu generieren. Da dies jedoch problematisch ist, ist es ein wenig willkürlich, aber der Code des Konvertierungsprozesses wird automatisch in der folgenden Form generiert. Ich würde es gerne probieren.

  1. Generieren Sie eine Struktur aus dem Antwortbeispiel einer Techie-API (Verwenden Sie den im vorherigen Artikel erstellten Code)
  2. Generieren Sie eine Struktur aus Yaml einer netten Prahlerei (mit go-swagger).

Ich möchte den Prozess der Konvertierung von src nach dst automatisch generieren, vorausgesetzt, der go-Code der beiden von diesen generierten Strukturen ist die Struktur des Antwortausdrucks von model bzw. api für die Persistenz.

Die automatische Generierung selbst wird wie folgt erstellt.

  1. Extrahieren Sie Informationen aus der Strukturdefinition der Quelle go extract :: go [src] => JSON [src]
  2. Extrahieren Sie Informationen aus der Strukturdefinition des Ziels go extract :: go [dst] => JSON [dst]
  3. Generieren Sie Go-Code aus den extrahierten Informationen convert :: JSON [src], json [dst] => go [convert]

Mit diesen beiden wird der Code des Konvertierungsprozesses von der Struktur der Konvertierungsquelle (src) zur Struktur des Konvertierungsziels (dst) generiert.

Go-Code generieren (src)

Nehmen Sie das Antwortbeispiel von api, um den authentifizierten Benutzer aus dem folgenden Teil des Github-Dokuments abzurufen. Es gibt keinen besonderen Grund, diese API zu wählen. Das ist alles, weil ich es irgendwie bemerkt habe.

Speichern Sie es als github-get-authenticated-user.json.

{
  "login": "octocat",
  "id": 1,
  "avatar_url": "https://github.com/images/error/octocat_happy.gif",
  "gravatar_id": "",
  "url": "https://api.github.com/users/octocat",
  "html_url": "https://github.com/octocat",
  "followers_url": "https://api.github.com/users/octocat/followers",
  "following_url": "https://api.github.com/users/octocat/following{/other_user}",
  "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
  "organizations_url": "https://api.github.com/users/octocat/orgs",
  "repos_url": "https://api.github.com/users/octocat/repos",
  "events_url": "https://api.github.com/users/octocat/events{/privacy}",
  "received_events_url": "https://api.github.com/users/octocat/received_events",
  "type": "User",
  "site_admin": false,
  "name": "monalisa octocat",
  "company": "GitHub",
  "blog": "https://github.com/blog",
  "location": "San Francisco",
  "email": "[email protected]",
  "hireable": false,
  "bio": "There once was...",
  "public_repos": 2,
  "public_gists": 1,
  "followers": 20,
  "following": 0,
  "created_at": "2008-01-14T04:33:35Z",
  "updated_at": "2008-01-14T04:33:35Z",
  "total_private_repos": 100,
  "owned_private_repos": 100,
  "private_gists": 81,
  "disk_usage": 10000,
  "collaborators": 8,
  "plan": {
    "name": "Medium",
    "space": 400,
    "private_repos": 20,
    "collaborators": 0
  }
}

Generieren Sie hier die Strukturdefinition. Verwenden Sie im vorherigen Artikel erstelltes Skript.

$ swagger_src
mkdir -p src/github
python src/jsontogo/jsontogo.py json/github-get-authenticated-user.json --package github --name AuthenticatedUser | gofmt > src/github/authenticated_user.go

Sie können die folgende Strukturdefinition erhalten. (Speichern als src / github / authenticated_user.go)

package github

import (
	"time"
)

/* structure
AuthenticatedUser
	Plan
*/
// AuthenticatedUser : auto generated JSON container
type AuthenticatedUser struct {
	AvatarURL         string    `json:"avatar_url" example:"https://github.com/images/error/octocat_happy.gif"`
	Bio               string    `json:"bio" example:"There once was..."`
	Blog              string    `json:"blog" example:"https://github.com/blog"`
	Collaborators     int       `json:"collaborators" example:"8"`
	Company           string    `json:"company" example:"GitHub"`
	CreatedAt         time.Time `json:"created_at" example:"2008-01-14T04:33:35Z"`
	DiskUsage         int       `json:"disk_usage" example:"10000"`
	Email             string    `json:"email" example:"[email protected]"`
	EventsURL         string    `json:"events_url" example:"https://api.github.com/users/octocat/events{/privacy}"`
	Followers         int       `json:"followers" example:"20"`
	FollowersURL      string    `json:"followers_url" example:"https://api.github.com/users/octocat/followers"`
	Following         int       `json:"following" example:"0"`
	FollowingURL      string    `json:"following_url" example:"https://api.github.com/users/octocat/following{/other_user}"`
	GistsURL          string    `json:"gists_url" example:"https://api.github.com/users/octocat/gists{/gist_id}"`
	GravatarID        string    `json:"gravatar_id" example:""`
	HTMLURL           string    `json:"html_url" example:"https://github.com/octocat"`
	Hireable          bool      `json:"hireable" example:"False"`
	ID                int       `json:"id" example:"1"`
	Location          string    `json:"location" example:"San Francisco"`
	Login             string    `json:"login" example:"octocat"`
	Name              string    `json:"name" example:"monalisa octocat"`
	OrganizationsURL  string    `json:"organizations_url" example:"https://api.github.com/users/octocat/orgs"`
	OwnedPrivateRepos int       `json:"owned_private_repos" example:"100"`
	Plan              Plan      `json:"plan"`
	PrivateGists      int       `json:"private_gists" example:"81"`
	PublicGists       int       `json:"public_gists" example:"1"`
	PublicRepos       int       `json:"public_repos" example:"2"`
	ReceivedEventsURL string    `json:"received_events_url" example:"https://api.github.com/users/octocat/received_events"`
	ReposURL          string    `json:"repos_url" example:"https://api.github.com/users/octocat/repos"`
	SiteAdmin         bool      `json:"site_admin" example:"False"`
	StarredURL        string    `json:"starred_url" example:"https://api.github.com/users/octocat/starred{/owner}{/repo}"`
	SubscriptionsURL  string    `json:"subscriptions_url" example:"https://api.github.com/users/octocat/subscriptions"`
	TotalPrivateRepos int       `json:"total_private_repos" example:"100"`
	Type              string    `json:"type" example:"User"`
	URL               string    `json:"url" example:"https://api.github.com/users/octocat"`
	UpdatedAt         time.Time `json:"updated_at" example:"2008-01-14T04:33:35Z"`
}

// Plan : auto generated JSON container
type Plan struct {
	Collaborators int    `json:"collaborators" example:"0"`
	Name          string `json:"name" example:"Medium"`
	PrivateRepos  int    `json:"private_repos" example:"20"`
	Space         int    `json:"space" example:"400"`
}

Jetzt haben Sie die Strukturdefinition für die Quelle (src).

Go-Code generieren (dst)

Als nächstes erhalten wir die Strukturdefinition für das Konvertierungsziel (dst). Rufen Sie die Go-Strukturdefinition mit go-swagger aus der Swagger-Definition ab.

api.gru

Es gibt einen Dienst namens api.gru. Dies ist ein Dienst, der die API-Spezifikationen von Webdiensten gemeinsam nutzt. Viele werden im OpenAPI / Swagger 2.0-Format bereitgestellt. Übrigens, wenn Sie sich swagger.yaml ansehen, das Sie mit ReDoc usw. hier abrufen können, können Sie verstehen, wie swagger ist.

api.gru

Versuchen wir die automatische Generierung am Beispiel der API von github. Laden Sie yaml of swagger definition von der folgenden URL herunter.

$ make swagger_fetch
mkdir -p yaml
wget https://api.apis.guru/v2/specs/github.com/v3/swagger.yaml -O yaml/github-swagger.yaml
gsed -i "s/type: *'null'/type: object/g; s/'+1':/'plus1':/g; s/'-1':/'minus1':/" yaml/github-swagger.yaml

Sie können [yaml like the link] erhalten (https://github.com/podhmo/advent2016/blob/master/yaml/github-swagger.yaml). Das sed after wget ist eine kleine Korrektur, um das aktuelle Problem mit go-swagger und das registrierte Problem mit swagger.yaml zu beheben.

Typinformationen aus der Strukturdefinition extrahieren

こちらは普通にgo-swaggerを実行するだけです。以下の様にして実行します。今回はstructの変換だけなのでmodelのみを生成します。本筋とは関係ないのですがなぜか"github.com/go-openapi/swag"のimportが出力されないという問題があったためgoimportsで追加しています。

$ make swagger_gen
rm -rf dst/swagger/gen
mkdir -p dst/swagger/gen
swagger generate model -f yaml/github-swagger.yaml --target dst/swagger/gen --model-package def
goimports -w dst/swagger/gen/def/*.go

Es wird viel Code generiert, aber diesmal kümmert sich nur User.go darum.

package def

// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command

import (
	strfmt "github.com/go-openapi/strfmt"
	"github.com/go-openapi/swag"

	"github.com/go-openapi/errors"
)

// User user
// swagger:model user
type User struct {

	// avatar url
	AvatarURL string `json:"avatar_url,omitempty"`

	// bio
	Bio string `json:"bio,omitempty"`

	// blog
	Blog string `json:"blog,omitempty"`

	// collaborators
	Collaborators int64 `json:"collaborators,omitempty"`

	// company
	Company string `json:"company,omitempty"`

	// ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ
	CreatedAt string `json:"created_at,omitempty"`

	// disk usage
	DiskUsage int64 `json:"disk_usage,omitempty"`

	// email
	Email string `json:"email,omitempty"`

	// followers
	Followers int64 `json:"followers,omitempty"`

	// following
	Following int64 `json:"following,omitempty"`

	// gravatar id
	GravatarID string `json:"gravatar_id,omitempty"`

	// hireable
	Hireable bool `json:"hireable,omitempty"`

	// html url
	HTMLURL string `json:"html_url,omitempty"`

	// id
	ID int64 `json:"id,omitempty"`

	// location
	Location string `json:"location,omitempty"`

	// login
	Login string `json:"login,omitempty"`

	// name
	Name string `json:"name,omitempty"`

	// owned private repos
	OwnedPrivateRepos int64 `json:"owned_private_repos,omitempty"`

	// plan
	Plan *UserPlan `json:"plan,omitempty"`

	// private gists
	PrivateGists int64 `json:"private_gists,omitempty"`

	// public gists
	PublicGists int64 `json:"public_gists,omitempty"`

	// public repos
	PublicRepos int64 `json:"public_repos,omitempty"`

	// total private repos
	TotalPrivateRepos int64 `json:"total_private_repos,omitempty"`

	// type
	Type string `json:"type,omitempty"`

	// url
	URL string `json:"url,omitempty"`
}

// Validate validates this user
func (m *User) Validate(formats strfmt.Registry) error {
	var res []error

	if err := m.validatePlan(formats); err != nil {
		// prop
		res = append(res, err)
	}

	if len(res) > 0 {
		return errors.CompositeValidationError(res...)
	}
	return nil
}

func (m *User) validatePlan(formats strfmt.Registry) error {

	if swag.IsZero(m.Plan) { // not required
		return nil
	}

	if m.Plan != nil {

		if err := m.Plan.Validate(formats); err != nil {
			return err
		}
	}

	return nil
}

// UserPlan user plan
// swagger:model UserPlan
type UserPlan struct {

	// collaborators
	Collaborators int64 `json:"collaborators,omitempty"`

	// name
	Name string `json:"name,omitempty"`

	// private repos
	PrivateRepos int64 `json:"private_repos,omitempty"`

	// space
	Space int64 `json:"space,omitempty"`
}

// Validate validates this user plan
func (m *UserPlan) Validate(formats strfmt.Registry) error {
	var res []error

	if len(res) > 0 {
		return errors.CompositeValidationError(res...)
	}
	return nil
}

Typinformationen aus struct extrahieren

Zumindest müssen Sie die Typinformationen aus dem Go-Code in der Strukturdefinition extrahieren. Lassen Sie uns in diesem Bereich unser Bestes geben. Wie viel, wenn Sie einen Geist mit Ast haben. Ich habe einen Befehl namens go-structjson erstellt. Erwarten Sie nicht zu viel Codequalität im Inneren, da dies ein Chaos ist.

Aus dem Code wie unten

package alias

// Person :
type Person struct {
	Name string
}

type P *Person
type PS []Person
type PS2 []P
type PSP *[]P

Gibt den folgenden JSON aus.

{
  "module": {
    "alias": {
      "file": {
        "GOPATH/src/github.com/podhmo/go-structjson/examples/alias/person.go": {
          "alias": {
            "P": {
              "candidates": null,
              "name": "P",
              "original": {
                "kind": "pointer",
                "value": {
                  "kind": "primitive",
                  "value": "Person"
                }
              }
            },
            "PS": {
              "candidates": null,
              "name": "PS",
              "original": {
                "kind": "array",
                "value": {
                  "kind": "primitive",
                  "value": "Person"
                }
              }
            },
            "PS2": {
              "candidates": null,
              "name": "PS2",
              "original": {
                "kind": "array",
                "value": {
                  "kind": "primitive",
                  "value": "P"
                }
              }
            },
            "PSP": {
              "candidates": null,
              "name": "PSP",
              "original": {
                "kind": "pointer",
                "value": {
                  "kind": "array",
                  "value": {
                    "kind": "primitive",
                    "value": "P"
                  }
                }
              }
            }
          },
          "name": "GOPATH/src/github.com/podhmo/go-structjson/examples/alias/person.go",
          "struct": {
            "Person": {
              "fields": {
                "Name": {
                  "embed": false,
                  "name": "Name",
                  "tags": {},
                  "type": {
                    "kind": "primitive",
                    "value": "string"
                  }
                }
              },
              "name": "Person"
            }
          }
        }
      },
      "fullname": "github.com/podhmo/go-structjson/examples/alias",
      "name": "alias"
    }
  }
}

Es gab eine Geschichte, dass der Name newtype besser für diejenigen geeignet ist, die vom Namensalias behandelt werden. Hier wird "Typ MyInt int" usw. als Typalias bezeichnet. Es tut uns leid.

Für jeden src und dst wird eine JSON-Datei ausgegeben.

$ make swagger_extract
mkdir -p dst/swagger/convert
mkdir -p json/extracted
go-structjson -target src/github > json/extracted/src.json
go-structjson -target dst/swagger/gen/def > json/extracted/dst.json

Generieren von Code für die Konvertierung aus dem extrahierten JSON mit Typinformationen

Nun, ich konnte den Code der Struktur der Konvertierungsquelle und des Konvertierungsziels abrufen. Als nächstes folgt die automatische Generierung der Konvertierungsverarbeitung. Ich mache gerade eine Bibliothek namens goconvert für diese Zeit. Die Tatsache, dass es gemacht wird, erscheint an der Stelle, an der nur zu tun ist, steht im Read me. Ich werde dies vorerst nutzen. Code zum Konvertieren von src in dst mit convert.py, der tatsächlich mit dieser Bibliothek erstellt wurde Generieren.

$make swagger_convert
mkdir -p dst/swagger/convert
python src/convert.py --logger=DEBUG --src json/extracted/src.json --dst json/extracted/dst.json --override json/extracted/convert.json > dst/swagger/convert/autogen.go || rm dst/swagger/convert/autogen.go
INFO:goconvert.builders:start register function AuthenticatedUserToUser: ['github.com/podhmo/advent2016/src/github.AuthenticatedUser'] -> ['github.com/podhmo/advent2016/dst/swagger/gen/def.User']
DEBUG:goconvert.builders:resolve: avatarurl ('string',) -> avatarurl ('string',)
DEBUG:goconvert.minicode:gencode: ('string',) -> ('string',)
...
NotImplementedError: mapping not found ('time.Time',) -> ('string',)

Oh. Es funktioniert nicht.

Ich kann den Code für string scheinbar nicht aus time.Time generieren. Als ich hineinschaute, schien es, als hätte ich Probleme beim Umgang mit Created At.

src (generiert aus der API-Antwort)

type AuthenticatedUser struct {
	AvatarURL         string    `json:"avatar_url" example:"https://github.com/images/error/octocat_happy.gif"`
...
	CreatedAt         time.Time `json:"created_at" example:"2008-01-14T04:33:35Z"`

dst (generiert aus swagger.yaml)

// User user
// swagger:model user
type User struct {

	// avatar url
	AvatarURL string `json:"avatar_url,omitempty"`

...
	// ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ
	CreatedAt string `json:"created_at,omitempty"`

Nun, das ist in gewissem Sinne natürlich und unvermeidlich. Unabhängig davon, wie viel Sie den Go-Typ-Informationen nachverfolgen, wissen Sie nicht, wie Sie time.Time in string konvertieren können. Nur der Entwickler weiß es. Schreiben Sie die folgende Zeit. Zeit-zu-Zeichenfolge-Konvertierungsprozess als primitive.go in dasselbe Paket wie das Ausgabeziel des Konvertierungsprozesses.

package convert

import "time"

// TimeToString :
func TimeToString(t time.Time) string {
	return t.Format(time.RFC3339)
}

Wiederholen Sie dann die JSON-Ausgabe. Dieses Mal wird auch go-funcjson hinzugefügt (durch Übergeben des von go-funcjson generierten JSON wird die handschriftliche Konvertierungsverarbeitungsfunktion wie zum Zeitpunkt der automatischen Generierung verwendet).

$ make swagger_extract
mkdir -p dst/swagger/convert
mkdir -p json/extracted
go-structjson -target src/github > json/extracted/src.json
go-structjson -target dst/swagger/gen/def > json/extracted/dst.json
go-funcjson -target dst/swagger/convert > json/extracted/convert.json || echo '{}' > json/extracted/convert.json
...
-- write: convert --
gofmt -w dst/swagger/convert/autogen.go

Diesmal war es erfolgreich. Der folgende Code wird generiert (dst / swagger / convert / autogen.go).

package convert

import (
	def "github.com/podhmo/advent2016/dst/swagger/gen/def"
	github "github.com/podhmo/advent2016/src/github"
)

// AuthenticatedUserToUser :
func AuthenticatedUserToUser(from *github.AuthenticatedUser) *def.User {
	to := &def.User{}
	to.AvatarURL = from.AvatarURL
	to.Bio = from.Bio
	to.Blog = from.Blog
	tmp1 := (int64)(from.Collaborators)
	to.Collaborators = tmp1
	to.Company = from.Company
	to.CreatedAt = TimeToString(from.CreatedAt)
	tmp2 := (int64)(from.DiskUsage)
	to.DiskUsage = tmp2
	to.Email = from.Email
	tmp3 := (int64)(from.Followers)
	to.Followers = tmp3
	tmp4 := (int64)(from.Following)
	to.Following = tmp4
	to.GravatarID = from.GravatarID
	to.Hireable = from.Hireable
	to.HTMLURL = from.HTMLURL
	tmp5 := (int64)(from.ID)
	to.ID = tmp5
	to.Location = from.Location
	to.Login = from.Login
	to.Name = from.Name
	tmp6 := (int64)(from.OwnedPrivateRepos)
	to.OwnedPrivateRepos = tmp6
	to.Plan = PlanToUserPlan(&(from.Plan))
	tmp7 := (int64)(from.PrivateGists)
	to.PrivateGists = tmp7
	tmp8 := (int64)(from.PublicGists)
	to.PublicGists = tmp8
	tmp9 := (int64)(from.PublicRepos)
	to.PublicRepos = tmp9
	tmp10 := (int64)(from.TotalPrivateRepos)
	to.TotalPrivateRepos = tmp10
	to.Type = from.Type
	to.URL = from.URL
	return to
}

// PlanToUserPlan :
func PlanToUserPlan(from *github.Plan) *def.UserPlan {
	to := &def.UserPlan{}
	tmp11 := (int64)(from.Collaborators)
	to.Collaborators = tmp11
	to.Name = from.Name
	tmp12 := (int64)(from.PrivateRepos)
	to.PrivateRepos = tmp12
	tmp13 := (int64)(from.Space)
	to.Space = tmp13
	return to
}

Das in primitive.go hinzugefügte "TimeToString ()" wird auch im Teil "CreatedAt" verwendet. Wird es kompiliert?

$ (cd dst/swagger/convert/; go install)

Es sieht okay aus.

Führen Sie den Konvertierungsprozess tatsächlich aus

Verwenden wir tatsächlich den generierten Konvertierungsverarbeitungscode. Führen Sie den folgenden Code aus.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/davecgh/go-spew/spew"
	"github.com/podhmo/advent2016/dst/swagger/convert"
	"github.com/podhmo/advent2016/src/github"
)

func main() {
	decoder := json.NewDecoder(os.Stdin)
	var from github.AuthenticatedUser
	if err := decoder.Decode(&from); err != nil {
		log.Fatal(err)
	}

	{
		fmt.Println("------------------------------")
		fmt.Println("source:")
		fmt.Println("------------------------------")
		spew.Dump(from)
	}
	{
		to := convert.AuthenticatedUserToUser(&from)
		fmt.Println("------------------------------")
		fmt.Println("destination:")
		fmt.Println("------------------------------")
		spew.Dump(to)
	}
}

Wenn der Konvertierungsverarbeitungscode erfolgreich generiert wurde, wird die Kompilierung bestanden. Sie sollten dazu in der Lage sein. Da dies problematisch ist, übergeben wir die Antwort der ursprünglich heruntergeladenen Github-API an die Eingabe.

$ make swagger_run1
cat json/github-get-authenticated-user.json | go run dst/swagger/main/spew/*.go
------------------------------
source:
------------------------------
(github.AuthenticatedUser) {
 AvatarURL: (string) (len=49) "https://github.com/images/error/octocat_happy.gif",
 Bio: (string) (len=17) "There once was...",
 Blog: (string) (len=23) "https://github.com/blog",
 Collaborators: (int) 8,
 Company: (string) (len=6) "GitHub",
 CreatedAt: (time.Time) 2008-01-14 04:33:35 +0000 UTC,
 DiskUsage: (int) 10000,
 Email: (string) (len=18) "[email protected]",
 EventsURL: (string) (len=53) "https://api.github.com/users/octocat/events{/privacy}",
 Followers: (int) 20,
 FollowersURL: (string) (len=46) "https://api.github.com/users/octocat/followers",
 Following: (int) 0,
 FollowingURL: (string) (len=59) "https://api.github.com/users/octocat/following{/other_user}",
 GistsURL: (string) (len=52) "https://api.github.com/users/octocat/gists{/gist_id}",
 GravatarID: (string) "",
 HTMLURL: (string) (len=26) "https://github.com/octocat",
 Hireable: (bool) false,
 ID: (int) 1,
 Location: (string) (len=13) "San Francisco",
 Login: (string) (len=7) "octocat",
 Name: (string) (len=16) "monalisa octocat",
 OrganizationsURL: (string) (len=41) "https://api.github.com/users/octocat/orgs",
 OwnedPrivateRepos: (int) 100,
 Plan: (github.Plan) {
  Collaborators: (int) 0,
  Name: (string) (len=6) "Medium",
  PrivateRepos: (int) 20,
  Space: (int) 400
 },
 PrivateGists: (int) 81,
 PublicGists: (int) 1,
 PublicRepos: (int) 2,
 ReceivedEventsURL: (string) (len=52) "https://api.github.com/users/octocat/received_events",
 ReposURL: (string) (len=42) "https://api.github.com/users/octocat/repos",
 SiteAdmin: (bool) false,
 StarredURL: (string) (len=59) "https://api.github.com/users/octocat/starred{/owner}{/repo}",
 SubscriptionsURL: (string) (len=50) "https://api.github.com/users/octocat/subscriptions",
 TotalPrivateRepos: (int) 100,
 Type: (string) (len=4) "User",
 URL: (string) (len=36) "https://api.github.com/users/octocat",
 UpdatedAt: (time.Time) 2008-01-14 04:33:35 +0000 UTC
}
------------------------------
destination:
------------------------------
(*def.User)(0xc4200ae140)({
 AvatarURL: (string) (len=49) "https://github.com/images/error/octocat_happy.gif",
 Bio: (string) (len=17) "There once was...",
 Blog: (string) (len=23) "https://github.com/blog",
 Collaborators: (int64) 8,
 Company: (string) (len=6) "GitHub",
 CreatedAt: (string) (len=20) "2008-01-14T04:33:35Z",
 DiskUsage: (int64) 10000,
 Email: (string) (len=18) "[email protected]",
 Followers: (int64) 20,
 Following: (int64) 0,
 GravatarID: (string) "",
 Hireable: (bool) false,
 HTMLURL: (string) (len=26) "https://github.com/octocat",
 ID: (int64) 1,
 Location: (string) (len=13) "San Francisco",
 Login: (string) (len=7) "octocat",
 Name: (string) (len=16) "monalisa octocat",
 OwnedPrivateRepos: (int64) 100,
 Plan: (*def.UserPlan)(0xc4204da1b0)({
  Collaborators: (int64) 0,
  Name: (string) (len=6) "Medium",
  PrivateRepos: (int64) 20,
  Space: (int64) 400
 }),
 PrivateGists: (int64) 81,
 PublicGists: (int64) 1,
 PublicRepos: (int64) 2,
 TotalPrivateRepos: (int64) 100,
 Type: (string) (len=4) "User",
 URL: (string) (len=36) "https://api.github.com/users/octocat"
})

Es scheint zu funktionieren. (Übrigens, wenn die Struktur genau gleich ist, ist es möglicherweise einfacher, den Wert über JSON mit json.Unmarshal nach json.Marshal zu konvertieren.)

Das Makefile für die für die Konvertierung verwendeten Befehle lautet hier.

Weitere Informationen, wenn der vorhandene Typ aktualisiert wird

Tatsächlich ist der Quellcode des Codes, der bisher bei der Codekonvertierung verwendet wurde, derselbe wie im vorherigen Artikel, aber nicht vollständig derselbe. Letzter Code Ich habe den folgenden Teil auskommentiert.

        # elif "://" in val:
        #     return "github.com/go-openapi/strfmt.Uri"

Kommentieren Sie dies aus und versuchen Sie es erneut zu generieren.

$ make swagger_src
...
$ make swagger_extract
...
$ make swagger_convert
...
KeyError: 'Uri'

Ich habe versagt. Dies liegt daran, dass die Bibliothek github.com/go-openapi/strfmt, die ich seit dem Schreiben des vorherigen Artikels verwende, aktualisiert wurde. tat. Insbesondere scheint sich "strfmt.Uri" in "strfmt.URI" geändert zu haben.

Lassen Sie es uns erneut aktualisieren und dann erneut generieren.

$ go get -u github.com/go-openapi/strfmt
$ gsed -i 's/strfmt.Uri/strfmt.URI/' src/jsontogo/jsontogo.py
$ make swagger_src
$ make swagger_extract
$ make swagger_convert

Diesmal scheint es gelungen zu sein. Natürlich können Sie auch Konvertierungen durchführen.

$ make swagger_run1
cat json/github-get-authenticated-user.json | go run dst/swagger/main/spew/*.go
------------------------------
source:
------------------------------
(github.AuthenticatedUser) {
 AvatarURL: (strfmt.URI) (len=49) https://github.com/images/error/octocat_happy.gif,
 Bio: (string) (len=17) "There once was...",
 Blog: (strfmt.URI) (len=23) https://github.com/blog,
 Collaborators: (int) 8,
 Company: (string) (len=6) "GitHub",
 CreatedAt: (time.Time) 2008-01-14 04:33:35 +0000 UTC,
 DiskUsage: (int) 10000,
 Email: (string) (len=18) "[email protected]",
 EventsURL: (strfmt.URI) (len=53) https://api.github.com/users/octocat/events{/privacy},
 Followers: (int) 20,
 FollowersURL: (strfmt.URI) (len=46) https://api.github.com/users/octocat/followers,
 Following: (int) 0,
 FollowingURL: (strfmt.URI) (len=59) https://api.github.com/users/octocat/following{/other_user},
 GistsURL: (strfmt.URI) (len=52) https://api.github.com/users/octocat/gists{/gist_id},
 GravatarID: (string) "",
 HTMLURL: (strfmt.URI) (len=26) https://github.com/octocat,
 Hireable: (bool) false,
 ID: (int) 1,
 Location: (string) (len=13) "San Francisco",
 Login: (string) (len=7) "octocat",
 Name: (string) (len=16) "monalisa octocat",
 OrganizationsURL: (strfmt.URI) (len=41) https://api.github.com/users/octocat/orgs,
 OwnedPrivateRepos: (int) 100,
 Plan: (github.Plan) {
  Collaborators: (int) 0,
  Name: (string) (len=6) "Medium",
  PrivateRepos: (int) 20,
  Space: (int) 400
 },
 PrivateGists: (int) 81,
 PublicGists: (int) 1,
 PublicRepos: (int) 2,
 ReceivedEventsURL: (strfmt.URI) (len=52) https://api.github.com/users/octocat/received_events,
 ReposURL: (strfmt.URI) (len=42) https://api.github.com/users/octocat/repos,
 SiteAdmin: (bool) false,
 StarredURL: (strfmt.URI) (len=59) https://api.github.com/users/octocat/starred{/owner}{/repo},
 SubscriptionsURL: (strfmt.URI) (len=50) https://api.github.com/users/octocat/subscriptions,
 TotalPrivateRepos: (int) 100,
 Type: (string) (len=4) "User",
 URL: (strfmt.URI) (len=36) https://api.github.com/users/octocat,
 UpdatedAt: (time.Time) 2008-01-14 04:33:35 +0000 UTC
}
------------------------------
destination:
------------------------------
(*def.User)(0xc42015e280)({
 AvatarURL: (string) (len=49) "https://github.com/images/error/octocat_happy.gif",
 Bio: (string) (len=17) "There once was...",
 Blog: (string) (len=23) "https://github.com/blog",
 Collaborators: (int64) 8,
 Company: (string) (len=6) "GitHub",
 CreatedAt: (string) (len=20) "2008-01-14T04:33:35Z",
 DiskUsage: (int64) 10000,
 Email: (string) (len=18) "[email protected]",
 Followers: (int64) 20,
 Following: (int64) 0,
 GravatarID: (string) "",
 Hireable: (bool) false,
 HTMLURL: (string) (len=26) "https://github.com/octocat",
 ID: (int64) 1,
 Location: (string) (len=13) "San Francisco",
 Login: (string) (len=7) "octocat",
 Name: (string) (len=16) "monalisa octocat",
 OwnedPrivateRepos: (int64) 100,
 Plan: (*def.UserPlan)(0xc420330420)({
  Collaborators: (int64) 0,
  Name: (string) (len=6) "Medium",
  PrivateRepos: (int64) 20,
  Space: (int64) 400
 }),
 PrivateGists: (int64) 81,
 PublicGists: (int64) 1,
 PublicRepos: (int64) 2,
 TotalPrivateRepos: (int64) 100,
 Type: (string) (len=4) "User",
 URL: (string) (len=36) "https://api.github.com/users/octocat"
})

abschließend

Dieses Mal habe ich den Code of go analysiert und den Code für die Konvertierung von einer Struktur in eine andere generiert. Übrigens war es mühsam, also hörte ich auf, detaillierte interne Geschichten zu schreiben. Ich habe Python für die Codegenerierung verwendet, aber es kann schöner und bequemer sein, mit go abzuschließen. Es gibt einige Themen, über die ich sprechen kann, wenn ich die Energie habe, über den Analyseteil des Zeigers, die Behandlung von Alias (neuer Typ), den Code, der Slices entspricht, zu sprechen, was zu tun ist, wenn die Korrespondenz der Konvertierung nicht gefunden wird, also kann ich sie schreiben, wenn ich Lust dazu habe Vielleicht.

Bonus

Ich werde einen Bonus hinzufügen, da der generierte Code keine Dynamik hat. Ich werde versuchen, es zu einem Zeiger zu machen, indem ich mit einigen Typdefinitionen von src spiele.

diff --git a/src/github/authenticated_user.go b/src/github/authenticated_user.go
index 8893144..7e04142 100644
--- a/src/github/authenticated_user.go
+++ b/src/github/authenticated_user.go
@@ -11,8 +11,8 @@ AuthenticatedUser
 */
 // AuthenticatedUser : auto generated JSON container
 type AuthenticatedUser struct {
-	AvatarURL         strfmt.URI `json:"avatar_url" example:"https://github.com/images/error/octocat_happy.gif"`
-	Bio               string     `json:"bio" example:"There once was..."`
+	AvatarURL         *******strfmt.URI `json:"avatar_url" example:"https://github.com/images/error/octocat_happy.gif"`
+	Bio               *********string     `json:"bio" example:"There once was..."`
 	Blog              strfmt.URI `json:"blog" example:"https://github.com/blog"`
 	Collaborators     int        `json:"collaborators" example:"8"`
 	Company           string     `json:"company" example:"GitHub"`
@@ -35,10 +35,10 @@ type AuthenticatedUser struct {
 	OrganizationsURL  strfmt.URI `json:"organizations_url" example:"https://api.github.com/users/octocat/orgs"`
 	OwnedPrivateRepos int        `json:"owned_private_repos" example:"100"`
 	Plan              Plan       `json:"plan"`
-	PrivateGists      int        `json:"private_gists" example:"81"`
-	PublicGists       int        `json:"public_gists" example:"1"`
-	PublicRepos       int        `json:"public_repos" example:"2"`
-	ReceivedEventsURL strfmt.URI `json:"received_events_url" example:"https://api.github.com/users/octocat/received_events"`
+	PrivateGists      *int        `json:"private_gists" example:"81"`
+	PublicGists       **int        `json:"public_gists" example:"1"`
+	PublicRepos       ***int        `json:"public_repos" example:"2"`
+	ReceivedEventsURL ****strfmt.URI `json:"received_events_url" example:"https://api.github.com/users/octocat/received_events"`
 	ReposURL          strfmt.URI `json:"repos_url" example:"https://api.github.com/users/octocat/repos"`
 	SiteAdmin         bool       `json:"site_admin" example:"False"`
 	StarredURL        strfmt.URI `json:"starred_url" example:"https://api.github.com/users/octocat/starred{/owner}{/repo}"`

Der generierte Code sieht folgendermaßen aus: Es scheint richtig zu funktionieren. Wenn Sie sich das vorstellen, gab es in diesem Beispiel keine Scheiben.

package convert

import (
	def "github.com/podhmo/advent2016/dst/swagger/gen/def"
	github "github.com/podhmo/advent2016/src/github"
)

// AuthenticatedUserToUser :
func AuthenticatedUserToUser(from *github.AuthenticatedUser) *def.User {
	to := &def.User{}
	if from.AvatarURL != nil {
		tmp1 := *(from.AvatarURL)
		if tmp1 != nil {
			tmp2 := *(tmp1)
			if tmp2 != nil {
				tmp3 := *(tmp2)
				if tmp3 != nil {
					tmp4 := *(tmp3)
					if tmp4 != nil {
						tmp5 := *(tmp4)
						if tmp5 != nil {
							tmp6 := *(tmp5)
							if tmp6 != nil {
								tmp7 := *(tmp6)
								tmp8 := (string)(tmp7)
								to.AvatarURL = tmp8
							}
						}
					}
				}
			}
		}
	}
	if from.Bio != nil {
		tmp9 := *(from.Bio)
		if tmp9 != nil {
			tmp10 := *(tmp9)
			if tmp10 != nil {
				tmp11 := *(tmp10)
				if tmp11 != nil {
					tmp12 := *(tmp11)
					if tmp12 != nil {
						tmp13 := *(tmp12)
						if tmp13 != nil {
							tmp14 := *(tmp13)
							if tmp14 != nil {
								tmp15 := *(tmp14)
								if tmp15 != nil {
									tmp16 := *(tmp15)
									if tmp16 != nil {
										tmp17 := *(tmp16)
										to.Bio = tmp17
									}
								}
							}
						}
					}
				}
			}
		}
	}
	tmp18 := (string)(from.Blog)
	to.Blog = tmp18
	tmp19 := (int64)(from.Collaborators)
	to.Collaborators = tmp19
	to.Company = from.Company
	to.CreatedAt = TimeToString(from.CreatedAt)
	tmp20 := (int64)(from.DiskUsage)
	to.DiskUsage = tmp20
	to.Email = from.Email
	tmp21 := (int64)(from.Followers)
	to.Followers = tmp21
	tmp22 := (int64)(from.Following)
	to.Following = tmp22
	to.GravatarID = from.GravatarID
	to.Hireable = from.Hireable
	tmp23 := (string)(from.HTMLURL)
	to.HTMLURL = tmp23
	tmp24 := (int64)(from.ID)
	to.ID = tmp24
	to.Location = from.Location
	to.Login = from.Login
	to.Name = from.Name
	tmp25 := (int64)(from.OwnedPrivateRepos)
	to.OwnedPrivateRepos = tmp25
	to.Plan = PlanToUserPlan(&(from.Plan))
	if from.PrivateGists != nil {
		tmp26 := *(from.PrivateGists)
		tmp27 := (int64)(tmp26)
		to.PrivateGists = tmp27
	}
	if from.PublicGists != nil {
		tmp28 := *(from.PublicGists)
		if tmp28 != nil {
			tmp29 := *(tmp28)
			tmp30 := (int64)(tmp29)
			to.PublicGists = tmp30
		}
	}
	if from.PublicRepos != nil {
		tmp31 := *(from.PublicRepos)
		if tmp31 != nil {
			tmp32 := *(tmp31)
			if tmp32 != nil {
				tmp33 := *(tmp32)
				tmp34 := (int64)(tmp33)
				to.PublicRepos = tmp34
			}
		}
	}
	tmp35 := (int64)(from.TotalPrivateRepos)
	to.TotalPrivateRepos = tmp35
	to.Type = from.Type
	tmp36 := (string)(from.URL)
	to.URL = tmp36
	return to
}

// PlanToUserPlan :
func PlanToUserPlan(from *github.Plan) *def.UserPlan {
	to := &def.UserPlan{}
	tmp37 := (int64)(from.Collaborators)
	to.Collaborators = tmp37
	to.Name = from.Name
	tmp38 := (int64)(from.PrivateRepos)
	to.PrivateRepos = tmp38
	tmp39 := (int64)(from.Space)
	to.Space = tmp39
	return to
}

<! - ## Aufgefallene Blätter aufheben !-- !-- misc !-- ! ---- Was ist, wenn ich einen Teil der Konvertierungsfunktion handschriftlich schreiben möchte? ! ---- Was ist Zeigerunterstützung? ! ---- Was ist die Schnittstellenunterstützung? ! --- Was ist die Entsprechung von Scheiben? ! ---- Was ist die Entsprechung des Typalias (neuer Typ)? ! ---- Was ist Einbettungsunterstützung? ! ---- Was ist die Entsprechung von nicht konvertierbaren Feldern? ! --- Unterstützt Notationsschwankungen (Id, ID, ...) ! --- Unterstützt es mehrere Argumentfunktionen? ! --- Was passiert, wenn eine neue handschriftliche Funktion ein zusätzliches Argument erfordert? ! ---- Funktioniert es, wenn Sie eine Funktion verwenden möchten, die Sie beim Auslassen der Besetzung definiert haben? ->

Recommended Posts

Schreiben wir Python-Code, der Go-Code analysiert und Go-Code generiert
Versuchen Sie, Python-Code zu schreiben, um Go-Code zu generieren. - Versuchen Sie, JSON-to-Go usw. zu portieren
Schreiben wir ein Python-Programm und führen es aus
Schreiben wir Python mitinema4d.
Erstellen Sie den Code, der in Python "A und vorgeben B" ausgibt
So schreiben Sie eine Meta-Klasse, die sowohl Python2 als auch Python3 unterstützt
Dieser Python-Code hat keine Klassen ...
[Python] Lass uns alle und jeden meistern
[PEP8] Übernehmen Sie den Python-Quellcode und schreiben Sie ihn ordentlich
[Python] Lassen Sie uns kurz über die Einschlussnotation schreiben
Dies und das von Python-Eigenschaften
Schreiben Sie Selentestcode in Python
[Python] Ich habe einen einfachen Code geschrieben, der automatisch AA generiert (Ascii Art).
Lassen Sie uns JavaScript wegwerfen und ein Web-Frontend in Python schreiben!
Erstellen wir eine Kundendatenbank, in der QR-Code automatisch in Python ausgegeben wird
Code lesen von faker, einer Bibliothek, die Testdaten in Python generiert
Installation von Visual Studio Code und Installation von Python
Schreiben Sie testgetriebenen FizzBuzz-Code mit Python doctest.
Versuchen wir es mit gRPC mit Go und Docker
Schreiben Sie die O_SYNC-Datei in C und Python
Lesen und schreiben Sie JSON-Dateien mit Python
Komprimieren Sie Python-Daten und schreiben Sie in SQLite
Ein netter Nimporter, der Nim und Python verbindet
Implementieren Sie die Wiederholung und Erkundung von Gedenkstätten in Python und Go
Python-Code, der zusammenhängende Leerzeichen in einem entfernt
Vergleichen Sie die XML-Parsing-Geschwindigkeiten mit Python und Go
[Python3] Lesen und Schreiben mit datetime isoformat mit json
Links und Memos von Python-Zeichencodezeichenfolgen
Lassen Sie uns EV3-Motoren und -Sensoren mit Python steuern
J / N-Verarbeitung mit Bash, Python und Go
Liste des zu verschiebenden und zu merkenden Python-Codes
Lassen Sie uns eine App erstellen, die ähnliche Bilder mit Python und Flask Part1 durchsuchen kann
Lassen Sie uns eine App erstellen, die ähnliche Bilder mit Python und Flask Part2 durchsuchen kann
mong - Ich habe versucht, den Code, der zufällig den Containernamen von Docker generiert, nach Python zu portieren. -