Unmarshal returns blank object due to encoding - go

I'm attempting to unmarshal a raw json string. There seems to be an error with encoding but I can't quite figure it out.
package main
import (
"encoding/json"
"fmt"
"log"
)
type Foo struct {
Transmission string `json:"transmission"`
Trim string `json:"trim"`
Uuid string `json:"uuid"`
Vin string `json:"vin"`
}
func main() {
var foo Foo
sample := `{
"transmission": "continuously\x20variable\x20automatic",
"trim": "SL",
"uuid" : "6993e4090a0e0ae80c59a76326e360a1",
"vin": "5N1AZ2MH6JN192059"
}`
err := json.Unmarshal([]byte(sample), &foo)
if err != nil {
log.Fatal(err)
}
fmt.Println(foo)
}
2009/11/10 23:00:00 invalid character 'x' in string escape code
It works if transmission entry is removed.
Here is a working playground.

Your input is not valid JSON. The JSON spec states that
All code points may be placed within the quotation marks except for the code points that must be escaped: quotation mark (U+0022), reverse solidus (U+005C), and the control characters U+0000 to U+001F.
Additionally, although there are two-character escape sequences, \x is not one of them, and thus it is being correctly interpreted as an invalid escape sequence by the Go parser. If you want to have a backslash literal in your JSON, it needs to be represented by \\ in the JSON input itself. See a modified version of your example: https://play.golang.org/p/JZdPJGpPR5q
(note that this is not an issue with your Go string literal since you're already using a raw (``) string literal—the JSON itself needs to have two backslashes.)

You can replace \x with \\x using string.Replace() function. Then, Unmarshal the replaced string. Here is a working example.

Related

Output json with os.stdout.write and a new line?

new to golang here. I wanted to simply output some json to stdout for debug purposes. I think I'm going about this all wrong. Here is what I have:
type SomeObject struct {
Thing1 string
Thing2 string
Thing3 otherStruct
}
...
someObject = &SomeObject{
Thing1: "hello",
Thing2: "world",
...
}
someObjectLogEntry, err := json.Marshal(someObject)
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(someObjectLogEntry)
When I run this, it outputs the json as one line, however my service also has a heartbeat going and so the two coincide and output both things in the same line, something like:
{/// All that json content }[GIN] 2022/03/16 - 02:07:16 | 200 | 1.16µs | 127.0.0.1 | GET "/heartbeat"
What's the correct way to do what I'm doing (simply constructing a json object and outputting it)? If i do fmt.println it will then print out the byte code. Thanks!
You need to output a line feed or newline (\n) after the JSON. The simplest approach is probably using fmt.Printf e.g. (playground)
type SomeObject struct {
Thing1 string
Thing2 string
}
someObject := &SomeObject{
Thing1: "hello",
Thing2: "world",
}
someObjectLogEntry, err := json.Marshal(someObject)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%s\n", someObjectLogEntry)
os.Stdout.Write([]byte("something else\n"))
The typical way to achieve this while using the Printf function from the fmt package is by including a newline character \n in the format string. You don’t necessarily need to write the data to standard output, because Printf does that for you.
Another option is to use the Println function from the fmt package which formats using the default formats for its operands writes to standard output. It’s necessary to convert your JSON bytes to string while using Println. Note that, spaces are always added between the operands and a newline is appended while using Println.
The reason that you see individual byte values while using fmt.Println is because a byte slice is printed out as uninterpreted bytes of the string or slice — the byte slice can contain anything at all, not just printable characters. On the other hand, os.Stdout.Write writes the byte slice to standard out and your terminal renders them properly because they are printable characters.
The cleanest approach is to use json.Encoder to os.Stdout.
json.Encoder already appends a newline to each message written and is more efficient than using fmt with json bytes.
You can re-use the json encoder instead of calling json.Marshal then some type of separate write for each message.
import (
"os"
"encoding/json"
)
type SomeObject struct {
Thing1 string
Thing2 string
Thing3 otherStruct
}
...
encoder := json.NewEncoder(os.Stdout)
someObject = &SomeObject{
Thing1: "hello",
Thing2: "world",
...
}
if err := encoder.Encode(&someObject); err != nil {
// handle error
}

How to convert the string representation of a Terraform set of strings to a slice of strings

I've a terratest where I get an output from terraform like so s := "[a b]". The terraform output's value = toset([resource.name]), it's a set of strings.
Apparently fmt.Printf("%T", s) returns string. I need to iterate to perform further validation.
I tried the below approach but errors!
var v interface{}
if err := json.Unmarshal([]byte(s), &v); err != nil {
fmt.Println(err)
}
My current implementation to convert to a slice is:
s := "[a b]"
s1 := strings.Fields(strings.Trim(s, "[]"))
for _, v:= range s1 {
fmt.Println("v -> " + v)
}
Looking for suggestions to current approach or alternative ways to convert to arr/slice that I should be considering. Appreciate any inputs. Thanks.
Actually your current implementation seems just fine.
You can't use JSON unmarshaling because JSON strings must be enclosed in double quotes ".
Instead strings.Fields does just that, it splits a string on one or more characters that match unicode.IsSpace, which is \t, \n, \v. \f, \r and .
Moeover this works also if terraform sends an empty set as [], as stated in the documentation:
returning [...] an empty slice if s contains only white space.
...which includes the case of s being empty "" altogether.
In case you need additional control over this, you can use strings.FieldsFunc, which accepts a function of type func(rune) bool so you can determine yourself what constitutes a "space". But since your input string comes from terraform, I guess it's going to be well-behaved enough.
There may be third-party packages that already implement this functionality, but unless your program already imports them, I think the native solution based on the standard lib is always preferrable.
unicode.IsSpace actually includes also the higher runes 0x85 and 0xA0, in which case strings.Fields calls FieldsFunc(s, unicode.IsSpace)
package main
import (
"fmt"
"strings"
)
func main() {
src := "[a b]"
dst := strings.Split(src[1:len(src)-1], " ")
fmt.Println(dst)
}
https://play.golang.org/p/KVY4r_8RWv6

How to Print ascii text in go like python does

how to print ascii-text in go language like python does
like picture shown below
Using python
Using Golang
The problem is that your text contains backtick (`), which happen to be delimiter character for golang's raw string literal. This situation is comparable to your python code had your text contains 3 consecutive double-quotes, which is the delimiter being used in your python code.
I don't see any quick escape from this situation without modifying your ascii text, as we don't have other options for raw string delimiter in golang like we have in python. You may want to store your ascii text in a text file and read it from there :
import (
....
....
"io/ioutil"
)
func banner() string {
b, err := ioutil.ReadFile("ascii.txt")
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
If you're ok with slight modification to the ascii text source, then you can temporarily use other character that isn't used anywhere else in the ascii text to represent backtick, and then do string replacement to put the actual backtick in place. Or, you can use fmt.Sprintf to supply the problematic backtick :
ascii := fmt.Sprintf(`....%c88b...`, '`')
fmt.Println(ascii)
// output:
// ....`88b...
Yes but you have to split lines with backtick and put them quoted into standard double quote ”.
... +
“888 6(, ` ‘ “ +
...

cmd line parameter string.Contains behaving differently from hardcoded parameter

I'm looking to get some clarification on why these two strings.Contains() calls behave differently.
package main
import (
"strings"
"os"
"errors"
"fmt"
)
func main() {
hardcoded := "col1,col2,col3\nval1,val2,val3"
if strings.Contains(hardcoded, "\n") == false {
panic(errors.New("The hardcoded string should contain a new line"))
}
fmt.Println("New line found in hardcoded string")
if len(os.Args) == 2 {
fmt.Println("parameter:", os.Args[1])
if strings.Contains(os.Args[1], "\n") == false {
panic(errors.New("The parameter string should contain a new line."))
}
fmt.Println("New line found in parameter string")
}
}
If I run this with
go run input-tester.go col1,col2,col3\\nval1,val2,val3
I get the following
New line found in hardcoded string
parameter: col1,col2,col3\nval1,val2,val3
panic: The parameter string should contain a new line.
goroutine 1 [running]:
panic(0x497100, 0xc42000e310)
/usr/local/go/src/runtime/panic.go:500 +0x1a1
main.main()
/home/user/Desktop/input-tester.go:21 +0x343
exit status 2
I can see that the string printed out is the same format as the string that is hardcoded yet the string.Contains() doesn't find the "\n".
I'm guessing this is an oversight on my part. Can anyone explain what I'm missing or misunderstanding?
It behaves differently because in hardcoded \n is considered as new line parameter.
And in command line arguments , argument type is string, where given condition is for "\n" which is considered as new line parameter.
Simply ` \n compaires with two consecutive characters "\" and "n" not with "\n" a new line character.
So for command line arguments use,
if strings.Contains(os.Args[1], `\n`) == false {
panic(errors.New("The parameter string should contain a new line."))
}
Reference : https://golang.org/ref/spec#String_literals
Raw string literals are character sequences between back quotes, as in foo. Within the quotes, any character may appear except back quote. The value of a raw string literal is the string composed of the uninterpreted (implicitly UTF-8-encoded) characters between the quotes; in particular, backslashes have no special meaning and the string may contain newlines.

Sscanf of multiple string fields in golang

I am trying to use sscanf to parse multiple string fields. Here is an example code snippet:
package main
import "fmt"
func main() {
var name, currency string
_, err := fmt.Sscanf("transaction benson: dollars", "transaction %s: %s", &name, &currency)
fmt.Println(err, name, currency)
}
The output is
input does not match format benson:
Program exited.
%s is greedy and gobbles up to the next space, which means it eats up the colon. After processing the %s, it then tries to scan in the colon, but wait, that’s already been consumed, and the next character is actually a space, not a colon! So it fails.
In C you’d get around this by using %[^:] rather than %s, but it appears Go doesn’t support this. You might need to find some way to parse your string without Sscanf.

Resources