我正在开发一个API,它需要运营商作为值的过滤器。例如。 &val=true&Amount>33"
。我认为内部表示将是map [string] struct {Operator string,Val [] string}但它似乎不受标准库的支持。
是否有支持此类查询的外部包?它是一个常见的用例(例如搜索API),所以我很惊讶我无法在godoc上找到任何东西。
答案 0 :(得分:2)
似乎用例并不像我想象的那么普遍。我实现了一个自定义"解析器"。希望这不违反任何RFC。欢迎任何补丁!
package query
import(
"net/url"
"strings"
)
//ParseQueryOp parses the URL-encoded query string and returns a map
// listing the values specified for each key. ParseQueryOp always returns
// a non-nil map containing all the valid query parameters found;
// err describes the first decoding error encountered, if any.
// If the query has operators (e.g. &amount>300) it is Add-ed in the map
// with a prefixed key ( {{prefix}}key ).
/*
e.g. for a query &Amount>300 the operator(">") is returnd
s := "query &Amount>300"
q, _ := ParseQueryOp(s, "_op_")
print(q.Get("_op_Amount")) // prints `>`
print(q.Get("Amount")) // prints 300
*/
func ParseQueryOp(query, prefix string) (m url.Values, err error){
m = make(url.Values)
for query != "" {
var opKey string
key := query
if i := strings.IndexAny(key, "&;"); i >= 0 {
key, query = key[:i], key[i+1:]
} else {
query = ""
}
if key == "" {
continue
}
value := ""
var err1 error
if i := strings.Index(key, ">="); i >= 0{
key, value = key[:i], key[i+2:]
opKey = prefix + key
opKey, err1 = url.QueryUnescape(opKey)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
m[opKey] = append(m[opKey], ">=")
}else if i = strings.Index(key, "<="); i >= 0{
key, value = key[:i], key[i+2:]
opKey = prefix + key
opKey, err1 = url.QueryUnescape(opKey)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
m[opKey] = append(m[opKey], "<=")
}else if i = strings.Index(key, "="); i >= 0{
key, value = key[:i], key[i+1:]
}else if i = strings.Index(key, ">"); i >= 0{
key, value = key[:i], key[i+1:]
opKey = prefix + key
opKey, err1 = url.QueryUnescape(opKey)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
m[opKey] = append(m[opKey], ">")
}else if i = strings.Index(key, "<"); i >= 0{
key, value = key[:i], key[i+1:]
opKey = prefix + key
opKey, err1 = url.QueryUnescape(opKey)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
m[opKey] = append(m[opKey], "<")
}
key, err1 = url.QueryUnescape(key)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
value, err1 = url.QueryUnescape(value)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
m[key] = append(m[key], value)
}
return m, err
}
还有一些测试
package query
import(
"testing"
"net/url"
)
type parseTest struct {
query string
out url.Values
}
var parseTests = []parseTest{
{
query: "a=1&b=2",
out: url.Values{"a": []string{"1"}, "b": []string{"2"}},
},
{
query: "a=1&a=2&a=banana",
out: url.Values{"a": []string{"1", "2", "banana"}},
},
{
query: "ascii=%3Ckey%3A+0x90%3E",
out: url.Values{"ascii": []string{"<key: 0x90>"}},
},
{
query: "a=1;b=2",
out: url.Values{"a": []string{"1"}, "b": []string{"2"}},
},
{
query: "a=1&a=2;a=banana",
out: url.Values{"a": []string{"1", "2", "banana"}},
},
{
query: "a=1&b>2",
out: url.Values{"a": []string{"1"}, "_op_b": []string{">"}, "b": []string{"2"}},
},
{
query: "a=1&b<2",
out: url.Values{"a": []string{"1"}, "_op_b": []string{"<"}, "b": []string{"2"}},
},
{
query: "a=1&b>=2",
out: url.Values{"a": []string{"1"}, "_op_b": []string{">="}, "b": []string{"2"}},
},
{
query: "a=1&b<=2",
out: url.Values{"a": []string{"1"}, "_op_b": []string{"<="}, "b": []string{"2"}},
},
}
func TestParseQueryOut(t *testing.T) {
for i, test := range parseTests {
form, err := ParseQueryOp(test.query, "_op_")
if err != nil {
t.Errorf("test %d: Unexpected error: %v", i, err)
continue
}
if len(form) != len(test.out) {
t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out))
}
for k, evs := range test.out {
vs, ok := form[k]
if !ok {
t.Errorf("test %d: Missing key %q", i, k)
continue
}
if len(vs) != len(evs) {
t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs))
continue
}
for j, ev := range evs {
if v := vs[j]; v != ev {
t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev)
}
}
}
}
}