自己動手,寫一個json2xml小工具

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

項目地址:json2xml

什麼是antlr

antlr(ANother Tool for Language Recognition)是一個強大的文法分析器產生工具,它可用於讀取,處理,執行和翻譯結構化的文本和二進位檔案。目前,該工具廣泛應用於學術和工業生產領域,同時也是眾多語言,工具和架構的基礎。
今天我們就用這個工具實現一個go語言版的json2xml轉換器;

antlr的作用

關於一門語言的文法描述叫做grammar, 該工具能夠為該語言產生文法解析器,並自動建立文法分析數AST,同時antlr也能自動產生數的遍曆器,極大地降低手工coding 文法解析器的成本;

實踐開始

言歸正傳,拿json2xml為例,實現一個工具;

安裝

以macOS為例

brew install antlr

編輯json語言解析文法

// Derived from http://json.orggrammar JSON;json:   object    |   array    ;object    :   '{' pair (',' pair)* '}'    # AnObject    |   '{' '}'                     # NullObject    ;array    :   '[' value (',' value)* ']'  # ArrayOfValues    |   '[' ']'                     # NullArray    ;pair:   STRING ':' value ;value    :   STRING        # String    |   NUMBER        # Atom    |   object      # ObjectValue    |   array          # ArrayValue    |   'true'        # Atom    |   'false'        # Atom    |   'null'        # Atom    ;LCURLY : '{' ;LBRACK : '[' ;STRING :  '"' (ESC | ~["\\])* '"' ;fragment ESC :   '\\' (["\\/bfnrt] | UNICODE) ;fragment UNICODE : 'u' HEX HEX HEX HEX ;fragment HEX : [0-9a-fA-F] ;NUMBER    :   '-'? INT '.' INT EXP?   // 1.35, 1.35E-9, 0.3, -4.5    |   '-'? INT EXP            // 1e10 -3e4    |   '-'? INT                // -3, 45    ;fragment INT :   '0' | '1'..'9' '0'..'9'* ; // no leading zerosfragment EXP :   [Ee] [+\-]? INT ; // \- since - means "range" inside [...]WS  :   [ \t\n\r]+ -> skip ;

上面是依照antlr4的文法格式編輯的檔案

  • antlr4檔案文法也比較簡單:

    • 以grammar關鍵字開頭,名字與檔案名稱相匹配
    • 文法分析器的規則必須以小寫字母開頭
    • 詞法分析器的規則必須用大寫開頭
    • | 管道符號分割同一語言規則的若干備選分支,使用圓括弧把一些符號組成子規則。
  • 涉及到的幾個專有名詞:

    • 語言: 語言即一個有效語句的集合,語句由片語組成,片語由子片語組成,一次迴圈類推;
    • 文法: 文法定義語言的語意規則, 文法中每條規則定義一種片語結構;
    • 文法分析樹: 以樹狀的形式代表的文法的階層;根結點對應文法規則的名字,葉子節點代表語句中的符號或者詞法符號。
    • 詞法分析器: 將輸入的字元序列分解成一系列的詞法符號。一個詞法分析器負責分析詞法;
    • 文法分析器: 檢查語句結構是否符合文法規範或者是否合法。分析的過程類似走迷宮,一般都是通過對比匹配完成。
    • 自頂向下文法分析器: 是文法分析器的一種實現,每條規則都對應文法分析器中的一個函數;
    • 前向預測: 文法分析器使用前向預測來進行決策判斷,具體指將輸入的符號於每個備選分支的起始字元進行比較;

產生解析基礎代碼

# antlr4 -Dlanguage=Go -package json2xml JSON.g4
使用antlr產生目標語言為Go, package名為json2xml的基礎代碼

產生的檔案如下:

$ tree├── JSON.g4├── JSON.interp             # 文法解析中間檔案├── JSON.tokens             # 文法分析tokens流檔案├── JSONLexer.interp        # 詞法分析中間檔案├── JSONLexer.tokens        # 詞法分析tokens流檔案├── json_base_listener.go   # 預設是listener模式檔案├── json_lexer.go           # 詞法分析器├── json_listener.go        # 抽象listener介面檔案├── json_parser.go          # parser解析器檔案

實現解析器(listener例子)

package mainimport (    "fmt"    "io/ioutil"    "log"    "os"    "strings"    "testing"    "c2j/parser/json2xml"    "github.com/antlr/antlr4/runtime/Go/antlr")func init() {    log.SetFlags(log.LstdFlags | log.Lshortfile)}type j2xConvert struct {    *json2xml.BaseJSONListener    xml map[antlr.Tree]string}func NewJ2xConvert() *j2xConvert {    return &j2xConvert{        &json2xml.BaseJSONListener{},        make(map[antlr.Tree]string),    }}func (j *j2xConvert) setXML(ctx antlr.Tree, s string) {    j.xml[ctx] = s}func (j *j2xConvert) getXML(ctx antlr.Tree) string {    return j.xml[ctx]}// j2xConvert methodsfunc (j *j2xConvert) ExitJson(ctx *json2xml.JsonContext) {    j.setXML(ctx, j.getXML(ctx.GetChild(0)));}func (j *j2xConvert) stripQuotes(s string) string {    if s == "" || ! strings.Contains(s, "\"") {        return s    }    return s[1 : len(s)-1]}func (j *j2xConvert) ExitAnObject(ctx *json2xml.AnObjectContext) {    sb := strings.Builder{}    sb.WriteString("\n")    for _, p := range ctx.AllPair() {        sb.WriteString(j.getXML(p))    }    j.setXML(ctx, sb.String())}func (j *j2xConvert) ExitNullObject(ctx *json2xml.NullObjectContext) {    j.setXML(ctx, "")}func (j *j2xConvert) ExitArrayOfValues(ctx *json2xml.ArrayOfValuesContext) {    sb := strings.Builder{}    sb.WriteString("\n")    for _, p := range ctx.AllValue() {        sb.WriteString("<element>")        sb.WriteString(j.getXML(p))        sb.WriteString("</element>")        sb.WriteString("\n")    }    j.setXML(ctx, sb.String())}func (j *j2xConvert) ExitNullArray(ctx *json2xml.NullArrayContext) {    j.setXML(ctx, "")}func (j *j2xConvert) ExitPair(ctx *json2xml.PairContext) {    tag := j.stripQuotes(ctx.STRING().GetText())    v := ctx.Value()    r := fmt.Sprintf("<%s>%s</%s>\n", tag, j.getXML(v), tag)    j.setXML(ctx, r)}func (j *j2xConvert) ExitObjectValue(ctx *json2xml.ObjectValueContext) {    j.setXML(ctx, j.getXML(ctx.Object()))}func (j *j2xConvert) ExitArrayValue(ctx *json2xml.ArrayValueContext) {    j.setXML(ctx, j.getXML(ctx.Array()))}func (j *j2xConvert) ExitAtom(ctx *json2xml.AtomContext) {    j.setXML(ctx, ctx.GetText())}func (j *j2xConvert) ExitString(ctx *json2xml.StringContext) {    j.setXML(ctx, j.stripQuotes(ctx.GetText()))}func TestJSON2XMLVisitor(t *testing.T) {    f, err := os.Open("testdata/json2xml/t.json")    if err != nil {        panic(err)    }    defer f.Close()    content, err := ioutil.ReadAll(f)    if err != nil {        panic(err)    }    // Setup the input    is := antlr.NewInputStream(string(content))    // Create lexter    lexer := json2xml.NewJSONLexer(is)    stream := antlr.NewCommonTokenStream(lexer, antlr.LexerDefaultTokenChannel)    // Create parser and tree    p := json2xml.NewJSONParser(stream)    p.BuildParseTrees = true    tree := p.Json()    // Finally AST tree    j2x := NewJ2xConvert()    antlr.ParseTreeWalkerDefault.Walk(j2x, tree)    log.Println(j2x.getXML(tree))}
上面代碼比較簡單,看注釋就好;

一般流程如下:

  • 建立輸入資料流
  • 建立詞法分析器
  • 產生token流,儲存詞法分析器產生的詞法符號tokens
  • 建立文法分析器parser,處理tokens
  • 然後針對文法規則,開始文法分析
  • 最後通過預設提供的Walker,進行AST的遍曆

其中針對中間產生的參數和結果如何存放?好辦,直接定義個map,map鍵以Tree存放;

xml map[antlr.Tree]string

Listener和Visitor

antlr產生的程式碼有兩種預設,預設是listener實現,要產生visitor,需要另加參數-visitor。
這兩種機制的區別在於,監聽器的方法會被antlr提供的遍曆器對象自動調用,而visitor模式的方法中,必須顯示調用visit方法來訪問子節點。如果忘記調用的話,對應的子樹就不會被訪問。

總結

antlr是一個強大的工具,能讓常見的文法解析工作事半功倍,效率極高。同時,該工具使文法分析過程和程式本身高度分離,提供足夠的靈活性和可操控性。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.