github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2015/json/roman_numerals.go (about) 1 // +build OMIT 2 3 package main 4 5 import ( 6 "encoding/json" 7 "fmt" 8 "log" 9 "strings" 10 ) 11 12 type romanNumeral int 13 14 var numerals = []struct { 15 s string 16 v int 17 }{ 18 {"M", 1000}, {"CM", 900}, 19 {"D", 500}, {"CD", 400}, 20 {"C", 100}, {"XC", 90}, 21 {"L", 50}, {"XL", 40}, 22 {"X", 10}, {"IX", 9}, 23 {"V", 5}, {"IV", 4}, 24 {"I", 1}, 25 } 26 27 func (n romanNumeral) String() string { 28 res := "" 29 v := int(n) 30 for _, num := range numerals { 31 res += strings.Repeat(num.s, v/num.v) 32 v %= num.v 33 } 34 return res 35 } 36 37 func parseRomanNumeral(s string) (romanNumeral, error) { 38 res := 0 39 for _, num := range numerals { 40 for strings.HasPrefix(s, num.s) { 41 res += num.v 42 s = s[len(num.s):] 43 } 44 } 45 return romanNumeral(res), nil 46 } 47 48 func (n romanNumeral) MarshalJSON() ([]byte, error) { 49 if n <= 0 { 50 return nil, fmt.Errorf("Romans had only natural (=>1) numbers") 51 } 52 return json.Marshal(n.String()) 53 } 54 55 func (n *romanNumeral) UnmarshalJSON(data []byte) error { 56 var s string 57 if err := json.Unmarshal(data, &s); err != nil { 58 return err 59 } 60 p, err := parseRomanNumeral(s) 61 if err == nil { 62 *n = p 63 } 64 return err 65 } 66 67 type Movie struct { 68 Title string 69 Year romanNumeral 70 } 71 72 func main() { 73 // Encoding 74 movies := []Movie{{"E.T.", 1982}, {"The Matrix", 1999}, {"Casablanca", 1942}} 75 res, err := json.MarshalIndent(movies, "", "\t") // HL 76 if err != nil { 77 log.Fatal(err) 78 } 79 fmt.Printf("Movies: %s\n", res) 80 81 // Decoding 82 var m Movie 83 inputText := `{"Title": "Alien", "Year":"MCMLXXIX"}` 84 if err := json.NewDecoder(strings.NewReader(inputText)).Decode(&m); err != nil { 85 log.Fatal(err) 86 } 87 fmt.Printf("%s was released in %d\n", m.Title, m.Year) 88 }