바둑 배우기 시도 - 유령이 휴고에게 2
소개
다시 오신 것을 환영합니다! 우리는 내보낸 Ghost 데이터베이스를 Markdown으로 변환하는 프로토타입 프로그램을 만드는 과정에 있습니다continuing. 최종 목표는 Hugo와 함께 shindakun.net을 시작하고 실행하는 것입니다. 지난 번에는 파일을 메모리로 읽고 JSON을 Go 구조체로 변환하는 데 주로 집중하여 매우 쉽게 처리했습니다. 거기에서 우리는 첫 번째 게시물을 인쇄했습니다.
포스트 데이터
요약하자면 게시물 필드 중 하나에 포함된 내용입니다.
{
"id": "60710b90705967038fe662d6",
"uuid": "71ba3d71-ac18-4f33-82f7-1962baa83a07",
"title": "db test",
"slug": "db-test",
"mobiledoc": "{\"version\":\"0.3.1\",\"markups\":[],\"atoms\":[],\"cards\":[[\"markdown\",{\"cardName\":\"card-markdown\",\"markdown\":\"\\n<strike>This is a db test</strike>.\\n\"}]],\"sections\":[[10,0]],\"ghostVersion\":\"3.0\"}",
"html": "<!--kg-card-begin: markdown--><p><strike>This is a db test</strike>.</p>\n<!--kg-card-end: markdown-->",
"comment_id": "2",
"plaintext": "This is a db test.",
"feature_image": null,
"featured": 0,
"type": "post",
"status": "published",
"locale": null,
"visibility": "public",
"email_recipient_filter": "none",
"author_id": "60710b8d705967038fe66214",
"created_at": "2004-08-09T19:11:20.000Z",
"updated_at": "2004-08-09T19:11:20.000Z",
"published_at": "2004-08-09T19:11:20.000Z",
"custom_excerpt": null,
"codeinjection_head": null,
"codeinjection_foot": null,
"custom_template": null,
"canonical_url": null
},
적절한 서론을 사용하여 게시물을 작성하는 데 필요한 대부분의 필드는 우리가 반환할 개체 내에 존재합니다. 제목, 슬러그, 게시 날짜 등을 볼 수 있습니다. Markdown이 포함된 섹션은 Mobiledoc이라는 형식으로 되어 있습니다.
모바일독
Ghost 문서 Mobiledoc에 따르면
…a standardised JSON-based document storage format, which forms the heart of publishing with Ghost.
JSON 개체에서 추출하고 정리하면 작업할 수 있는 또 다른 JSON이 생깁니다.
{
"version": "0.3.1",
"markups": [],
"atoms": [],
"cards": [
[
"markdown",
{
"cardName": "card-markdown",
"markdown": "\n<strike>This is a db test</strike>.\n"
}
]
],
"sections": [
[
10,
0
]
],
"ghostVersion": "3.0"
}
쉬운 소리
먼저 우리가 가장 좋아하는 사이트인 JSON to Go 변환기를 사용하여 JSON 개체를 작업할 수 있는 구조체로 변환합니다.
type Mobiledoc struct {
Version string `json:"version"`
Markups []interface{} `json:"markups"`
Atoms []interface{} `json:"atoms"`
Cards [][]interface{} `json:"cards"`
Sections [][]int `json:"sections"`
GhostVersion string `json:"ghostVersion"`
}
이제 코드가 꽤 길어서 다른 구조체
GhostDatabase
구조체는 생략하겠습니다. 그래도 아래의 전체 코드 목록에 표시됩니다. 우리는 여전히 디코딩 로직을 작업하고 있기 때문에 여전히 코드를 화면에 덤프할 것입니다.package main
import (
"encoding/json"
"fmt"
"io"
"os"
"strconv"
"time"
)
type GhostDatabase struct {...}
type Mobiledoc struct {
Version string `json:"version"`
Markups []interface{} `json:"markups"`
Atoms []interface{} `json:"atoms"`
Cards [][]interface{} `json:"cards"`
Sections [][]int `json:"sections"`
GhostVersion string `json:"ghostVersion"`
}
func main() {
fmt.Println("ghost2hugo")
file, err := os.Open("shindakun-dot-net.ghost.2022-03-18-22-02-58.json")
if err != nil {
fmt.Println(err)
}
defer file.Close()
b, err := io.ReadAll(file)
if err != nil {
fmt.Println(err)
}
var db GhostDatabase
err = json.Unmarshal(b, &db)
if err != nil {
fmt.Println(err)
}
작업이 완료되면 "데이터베이스"를 반복하는 문제이기 때문에 지금은 첫 번째 게시물에 계속 초점을 맞추겠습니다. 이것은 약간 까다로워지는 곳입니다. 우리는 우리가 사용하는 적절한 섹션
db.Db[0].Data.Posts[0].Mobiledoc
으로 내려가기 위해 두 개의 중첩 배열로 작업하고 있습니다. 이렇게 하면 JSON 개체의 이스케이프된 버전을 사용할 수 있습니다."{\"version\":\"0.3.1\",\"markups\":[],\"atoms\":[],\"cards\":[[\"markdown\",{\"cardName\":\"card-markdown\",\"markdown\":\"\\n<strike>This is a db test</strike>.\\n\"}]],\"sections\":[[10,0]],\"ghostVersion\":\"3.0\"}"
여행을 떠나자
나는 우리가 얻는 문자열을 이스케이프 해제하는 방법이 있다는 것을 알고 있었고 Go documentation 을 확인하면
strconv.Unquote
로 연결되었습니다.Unquote interprets s as a single-quoted, double-quoted, or backquoted Go string literal, returning the string value that s quotes. (If s is single-quoted, it would be a Go character literal; Unquote returns the corresponding one-character string.)
정확히 우리에게 필요한 것! 문자열을 인용 해제하려고 할 때
invalid syntax
오류가 계속 수신된다는 점을 제외하고. 이것은 나를 조금 혼란스럽게 만들었습니다. 그것에 대해 수수께끼를 푼 후에 문자열 앞에 백틱을 추가하면 원시 문자열 리터럴로 취급되는 것 같다는 것을 깨달았습니다. 이로 인해 다음과 같은 코드가 생성됩니다. c := "`" + db.Db[0].Data.Posts[0].Mobiledoc + "`"
un, err := strconv.Unquote(c)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%v\n", un)
드디어! JSON이 있습니다!
{"version":"0.3.1","markups":[],"atoms":[],"cards":[["markdown",{"cardName":"card-markdown","markdown":"\n<strike>This is a db test</strike>.\n"}]],"sections":[[10,0]],"ghostVersion":"3.0"}
이제 이전에 설정한 Mobiledoc 구조체로 언마샬링할 수 있습니다!
var md Mobiledoc
err = json.Unmarshal([]byte(un), &md)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%#v", md)
덤프된 결과를 확인하면 예상 데이터가 있음을 알 수 있습니다.
main.Mobiledoc{Version:"0.3.1", Markups:[]interface {}{}, Atoms:[]interface {}{}, Cards:[][]interface {}{[]interface {}{"markdown", map[string]interface {}{"cardName":"card-markdown", "markdown":"\n<strike>This is a db test</strike>.\n"}}}, Sections:[][]int{[]int{10, 0}}, GhostVersion:"3.0"}
지금 우리는 순진하게 액세스하려는 경우 다음을 사용할 수 있는
Cards
라는 섹션에 관심이 있습니다. card := md.Cards[0][1]
fmt.Printf("\n\ncard: %#v\n", card)
card: map[string]interface {}{"cardName":"card-markdown", "markdown":"\n<strike>This is a db test</strike>.\n"}
좀 더 접근하기 쉬운 것으로 변환해 봅시다.
bbb := card.(map[string]interface{})
fmt.Println(bbb["markdown"])
<strike>This is a db test</strike>.
다음번
지금까지는 예상대로 첫 번째 게시물에서 Markdown을 추출했습니다. 다음에 우리는 모든 게시물을 추출하기 시작하는 작은 루프를 작성할 것입니다. 재미가 시작될 것 같은 느낌이 듭니다.
이 게시물을 즐기십니까?
How about buying me a coffee?
코드 목록
package main
import (
"encoding/json"
"fmt"
"io"
"os"
"strconv"
"time"
)
type GhostDatabase struct {
Db []struct {
Meta struct {
ExportedOn int64 `json:"exported_on"`
Version string `json:"version"`
} `json:"meta"`
Data struct {
Posts []struct {
ID string `json:"id"`
UUID string `json:"uuid"`
Title string `json:"title"`
Slug string `json:"slug"`
Mobiledoc string `json:"mobiledoc"`
HTML string `json:"html"`
CommentID string `json:"comment_id"`
Plaintext string `json:"plaintext"`
FeatureImage interface{} `json:"feature_image"`
Featured int `json:"featured"`
Type string `json:"type"`
Status string `json:"status"`
Locale interface{} `json:"locale"`
Visibility string `json:"visibility"`
EmailRecipientFilter string `json:"email_recipient_filter"`
AuthorID string `json:"author_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PublishedAt time.Time `json:"published_at"`
CustomExcerpt interface{} `json:"custom_excerpt"`
CodeinjectionHead interface{} `json:"codeinjection_head"`
CodeinjectionFoot interface{} `json:"codeinjection_foot"`
CustomTemplate interface{} `json:"custom_template"`
CanonicalURL interface{} `json:"canonical_url"`
} `json:"posts"`
PostsAuthors []struct {
ID string `json:"id"`
PostID string `json:"post_id"`
AuthorID string `json:"author_id"`
SortOrder int `json:"sort_order"`
} `json:"posts_authors"`
PostsMeta []interface{} `json:"posts_meta"`
PostsTags []struct {
ID string `json:"id"`
PostID string `json:"post_id"`
TagID string `json:"tag_id"`
SortOrder int `json:"sort_order"`
} `json:"posts_tags"`
Roles []struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
} `json:"roles"`
RolesUsers []struct {
ID string `json:"id"`
RoleID string `json:"role_id"`
UserID string `json:"user_id"`
} `json:"roles_users"`
Settings []struct {
ID string `json:"id"`
Group string `json:"group"`
Key string `json:"key"`
Value string `json:"value"`
Type string `json:"type"`
Flags interface{} `json:"flags"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
} `json:"settings"`
Tags []struct {
ID string `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Description interface{} `json:"description"`
FeatureImage interface{} `json:"feature_image"`
ParentID interface{} `json:"parent_id"`
Visibility string `json:"visibility"`
OgImage interface{} `json:"og_image"`
OgTitle interface{} `json:"og_title"`
OgDescription interface{} `json:"og_description"`
TwitterImage interface{} `json:"twitter_image"`
TwitterTitle interface{} `json:"twitter_title"`
TwitterDescription interface{} `json:"twitter_description"`
MetaTitle interface{} `json:"meta_title"`
MetaDescription interface{} `json:"meta_description"`
CodeinjectionHead interface{} `json:"codeinjection_head"`
CodeinjectionFoot interface{} `json:"codeinjection_foot"`
CanonicalURL interface{} `json:"canonical_url"`
AccentColor interface{} `json:"accent_color"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
} `json:"tags"`
Users []struct {
ID string `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Password string `json:"password"`
Email string `json:"email"`
ProfileImage string `json:"profile_image"`
CoverImage interface{} `json:"cover_image"`
Bio interface{} `json:"bio"`
Website interface{} `json:"website"`
Location interface{} `json:"location"`
Facebook interface{} `json:"facebook"`
Twitter interface{} `json:"twitter"`
Accessibility string `json:"accessibility"`
Status string `json:"status"`
Locale interface{} `json:"locale"`
Visibility string `json:"visibility"`
MetaTitle interface{} `json:"meta_title"`
MetaDescription interface{} `json:"meta_description"`
Tour interface{} `json:"tour"`
LastSeen time.Time `json:"last_seen"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
} `json:"users"`
} `json:"data"`
} `json:"db"`
}
type Mobiledoc struct {
Version string `json:"version"`
Markups []interface{} `json:"markups"`
Atoms []interface{} `json:"atoms"`
Cards [][]interface{} `json:"cards"`
Sections [][]int `json:"sections"`
GhostVersion string `json:"ghostVersion"`
}
func main() {
fmt.Println("ghost2hugo")
file, err := os.Open("shindakun-dot-net.ghost.2022-03-18-22-02-58.json")
if err != nil {
fmt.Println(err)
}
defer file.Close()
b, err := io.ReadAll(file)
if err != nil {
fmt.Println(err)
}
var db GhostDatabase
err = json.Unmarshal(b, &db)
if err != nil {
fmt.Println(err)
}
c := "`" + db.Db[0].Data.Posts[0].Mobiledoc + "`"
un, err := strconv.Unquote(c)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%v", un)
var md Mobiledoc
err = json.Unmarshal([]byte(un), &md)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%#v", md)
card := md.Cards[0][1]
fmt.Printf("\n\ncard: %#v\n", card)
bbb := card.(map[string]interface{})
fmt.Println(bbb["markdown"])
}
Reference
이 문제에 관하여(바둑 배우기 시도 - 유령이 휴고에게 2), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/shindakun/attempting-to-learn-go-ghost-to-hugo-2-3ei1텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)