I'm using Go for the first time and am trying to create a web application that takes in a receipt using json, and then generates a unique id for that receipt as well as allocates some "points" to that receipt based on some criteria. However when I enter in my json receipt into my form and hit "submit" I get the following error: invalid character 'r' looking for beginning of value.
I've printed out my receipt after it should get populated and it's empty so I'm aware that I'm just not serializing it properly. However I don't know how to serialize the json properly using go.
Here is my main.go
package main
import (
"encoding/json"
"fmt"
"html/template"
"math"
"math/rand"
"net/http"
"strconv"
"strings"
)
type Receipt struct {
Retailer string `json:"retailer"`
PurchaseDate string `json:"purchaseDate"`
PurchaseTime string `json:"purchaseTime"`
Items []Item `json:"items"`
Total string `json:"total"`
}
type Item struct {
ShortDescription string `json:"shortDescription"`
Price string `json:"price"`
}
type ReceiptResult struct {
ID string
Points int
}
func main() {
http.HandleFunc("/", receiptHandler)
http.ListenAndServe(":8080", nil)
}
func receiptHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
var receipt Receipt
err := json.NewDecoder(r.Body).Decode(&receipt)
j, _ := json.MarshalIndent(receipt, "", " ")
fmt.Println(string(j))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
result := processReceipt(receipt)
tmpl := template.Must(template.ParseFiles("result.html"))
tmpl.Execute(w, result)
} else {
tmpl := template.Must(template.ParseFiles("index.html"))
tmpl.Execute(w, nil)
}
}
func processReceipt(receipt Receipt) ReceiptResult {
var result ReceiptResult
result.ID = generateID()
result.Points += len(strings.ReplaceAll(receipt.Retailer, " ", ""))
result.Points += roundDollarPoints(receipt.Total)
result.Points += quarterPoints(receipt.Total)
result.Points += itemPoints(receipt.Items)
result.Points += itemDescriptionPoints(receipt.Items)
return result
}
func generateID() string {
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
b := make([]byte, 10)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
func roundDollarPoints(total string) int {
f, err := strconv.ParseFloat(total, 64)
if err != nil {
return 0
}
if math.Mod(f, 1) == 0 {
return 50
}
return 0
}
func quarterPoints(total string) int {
f, err := strconv.ParseFloat(total, 64)
if err != nil {
return 0
}
if math.Mod(f*100, 25) == 0 {
return 25
}
return 0
}
func itemPoints(items []Item) int {
return len(items) / 2 * 5
}
func itemDescriptionPoints(items []Item) int {
var points int
for _, item := range items {
trimmedLength := len(strings.TrimSpace(item.ShortDescription))
if trimmedLength%3 == 0 {
price, err := strconv.ParseFloat(item.Price, 64)
if err != nil {
continue
}
points += int(math.Ceil(price * 0.2))
}
}
return points
}
Here is my index.html, which is my home page
<!DOCTYPE html>
<html>
<head>
<title>Receipt Processor</title>
</head>
<body>
<h1>Receipt Processor</h1>
<form method="POST" action="/">
<label for="receipt">Receipt JSON:</label>
<textarea id="receipt" name="receipt" rows="10" cols="50"></textarea><br><br>
<input type="submit" value="Process Receipt">
</form>
</body>
</html>
Here is my result.html which is the page that I should get sent to after submitting my json
<!DOCTYPE html>
<html>
<head>
<title>Receipt Processor Result</title>
</head>
<body>
<h1>Receipt Processor Result</h1>
<p>Receipt ID: {{.ID}}</p>
<p>Points Earned: {{.Points}}</p>
</body>
</html>
This is the json I've been using for testing:
{
"retailer": "M&M Corner Market",
"purchaseDate": "2022-03-20",
"purchaseTime": "14:33",
"items": [
{
"shortDescription": "Gatorade",
"price": "2.25"
},
{
"shortDescription": "Doritos",
"price": "1.99"
},
{
"shortDescription": "Hershey's Chocolate Bar",
"price": "1.50"
},
{
"shortDescription": "Coca-Cola",
"price": "2.25"
}
],
"total": "8.99"
}