迭代从PHP序列化格式解码的地图

How I can read conditions unserialised data in golang in map format?

[map[19:map[conditions:map[0:map[operator:== value:AMW-1900-50SLE-ROOM 
is_value_processed:false type:feedexport/rule_condition_product 
attribute:sku] 1:map[type:feedexport/rule_condition_product 
attribute:sku operator:== value:ASL-B654-77-74-98-ROOM 
is_value_processed:false] 2:map[is_value_processed:false 
type:feedexport/rule_condition_product attribute:sku operator:== 
value:ASL-B217-57-54S-95-ROOM]] type:feedexport/rule_condition_combine 
attribute:<nil> operator:<nil> value:1 is_value_processed:<nil> 
aggregator:any]]]

This is the code:

package main

import (
    "fmt"
    "github.com/wulijun/go-php-serialize/phpserialize"
)

func main() {
    rules := RulesList()
    for _, rule := range rules{
        fmt.Println(rule.Conditions.(interface{}))
    }
}

type Rule struct {
    RuleId     int         `json:"rule_id"`
    Conditions interface{} `json:"conditions"`
}

func RulesList() ([]Rule) {
    db := DbConn()
    res, err := db.Query(`SELECT r.rule_id, r.conditions_serialized AS 
    conditions FROM m_feedexport_rule AS r`)
    CheckError(err)
    rule  := Rule{}
    rules := []Rule{}
    for res.Next()  {
        var ruleId int
        var conditions string
        err = res.Scan(&ruleId, &conditions)
        CheckError(err)
        cons, err := phpserialize.Decode(conditions)
        CheckError(err)
        rule.RuleId     = ruleId
        rule.Conditions = cons   
        rules = append(rules, rule)
    }
    return rules
}

The result is ok but I need it in map form, now this is the interface which I can't loop over. In case if anyone don't understand the code, ask me. Thanks a lot.

You're talking about the type of the variable cons, are you?

Background

If yes, the reason its type is interface{} is because in PHP, it's possible to serialize a value of any type (from bare integer to a complicated object), and hence any deserialization code must cope with it. Since in Go the so-called "empty interface", interface{}, is satisfied by any type at all (including any custom type implemented by a programmer), it's sensbile for a decoder of PHP-serialized data to return a value of type interface{}.

Solution

After making sure decoding succeeded, you need to either type-assert the resulting value to a type you need or to use a type switch to diverge processing based on the concrete type of the value returned by the decoder.

The approach is very well demonstrated by the package you're using in its own test suite.

A snippet from it demonstrating the basic approach

if decodeRes, err = Decode(result); err != nil {
    t.Errorf("decode data fail %v, %v", err, result)
    return
}
decodeData, ok := decodeRes.(map[interface{}]interface{})
if !ok {
    t.Errorf("decode data type error, expected: map[interface{}]interface{}, get: %T", decodeRes)
    return
}
obj, _ := decodeData["object"].(*PhpObject)
if v, _ := obj.GetPrivateMemberValue("a"); v != int64(1) {
    t.Errorf("object private value expected 1, get %v, %T", v, v)
}
if v := obj.GetClassName(); v != "A" {
    t.Errorf("object class name expected A, get %v", v)
}

Here, decodeRes is what returned by the decoder. That value is then type-asserted to make sure it's concrete type (also called "dynamic" — meaning "at runtime") is map[interface{}]interface{}.

Note that the so-called "two-argument" type assert is used (it's also called a "comma-ok idiom") to not make the program panic at runtime in case the concrete type is different from the expected (always do this on data fetched from outside!).

The value of the type-asserted concrete type is stored in the decodeData variable, and then that variable is inspected further.