第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

將外部 JSON 負載反序列化為 protobuf Any

將外部 JSON 負載反序列化為 protobuf Any

Go
FFIVE 2022-12-19 18:13:16
我有一個 protobuf 定義來處理來自 API 的分頁結果:message ArrayRespone {    int32 count = 1;    string next_url = 2;    string request_id = 3;    repeated google.protobuf.Any results = 4;    string status = 5;}此處的目標是反序列化來自此 API 的分頁響應,然后將每個頁面的結果提取到適當類型的切片中。我在 Go 中編寫了執(zhí)行此操作的代碼:func getData[T ~proto.Message](data []byte) ([]T, error) {    var resp *ArrayRespone    if err := json.Unmarshal(data, &resp); err != nil {        return nil, err    }        var items []T    for _, result := range resp.Results {        var item T        if err := result.UnmarshalTo(item); err != nil {            return nil, err        }        items = append(items, item)    }    return items, nil}我遇到的問題是,在測試這段代碼時,我遇到了以下錯誤:原型:不匹配的消息類型:得到“X”,想要“”由此,我可以理解 Protobuf 沒有必要的信息來確定它使用的是哪種類型。查看 的定義Any,我可以看到它有一個TypeUrl字段和一個Value字段。類型 URL 似乎為空但不應該為空。所以,我的想法是,如果我將它設置為X,錯誤就會消失,但這也行不通,因為該Value字段仍然是空的;我的 JSON 數(shù)據(jù)被忽略了。我怎樣才能讓這段代碼工作?
查看完整描述

1 回答

?
慕斯709654

TA貢獻1840條經驗 獲得超5個贊

我找到了這個問題的兩個潛在解決方案,但它們都涉及UnmarshalJSON. 首先,我嘗試修改我的原型定義,使其results成為 類型bytes,但 JSON 反序列化失敗,因為源數(shù)據(jù)不是字符串或任何可以[]byte直接反序列化的數(shù)據(jù)。所以,我不得不自己動手:


使用結構

使用該google.protobuf.Struct類型,我將我的修改ArrayResponse為如下所示:


message ArrayRespone {

    int32 count = 1;

    string next_url = 2;

    string request_id = 3;

    repeated google.protobuf.Struct results = 4;

    string status = 5;

}

然后編寫了一個自定義實現(xiàn),UnmarshalJSON其工作方式如下:


// UnmarshalJSON converts JSON data into a Providers.Polygon.ArrayResponse

func (resp *ArrayRespone) UnmarshalJSON(data []byte) error {


    // First, deserialize the JSON into a mapping between key fields and values

    // If this fails then return an error

    var mapped map[string]interface{}

    if err := json.Unmarshal(data, &mapped); err != nil {

        return fmt.Errorf("failed to perform first-pass unmarshal, error: %v", err)

    }


    // Next, extract the count from the mapping; if this fails return an error

    if err := extractValue(mapped, "count", &resp.Count); err != nil {

        return err

    }


    // Extract the next URL from the mapping; if this fails return an error

    if err := extractValue(mapped, "next_url", &resp.NextUrl); err != nil {

        return err

    }


    // Extract the request ID from the mapping; if this fails return an error

    if err := extractValue(mapped, "request_id", &resp.RequestId); err != nil {

        return err

    }


    // Extract the status from the mapping; if this fails return an error

    if err := extractValue(mapped, "status", &resp.Status); err != nil {

        return err

    }


    // Now, extract the results array into a temporary variable; if this fails return an error

    var results []interface{}

    if err := extractValue(mapped, "results", &results); err != nil {

        return err

    }


    // Finally, iterate over each result and add it to the slice of results by attempting

    // to convert it to a Struct; if any of these fail to convert then return an error

    resp.Results = make([]*structpb.Struct, len(results))

    for i, result := range results {

        if value, err := structpb.NewStruct(result.(map[string]interface{})); err == nil {

            resp.Results[i] = value

        } else {

            return fmt.Errorf("failed to create struct from result %d, error: %v", i, err)

        }

    }


    return nil

}


// Helper function that attempts to extract a value from a standard mapping of interfaces

// and set a field with it if the types are compatible

func extractValue[T any](mapping map[string]interface{}, field string, value *T) error {

    if raw, ok := mapping[field]; ok {

        if inner, ok := raw.(T); ok {

            *value = inner

        } else {

            return fmt.Errorf("failed to set value %v to field %s (%T)", raw, field, *value)

        }

    }


    return nil

}

然后,在我的服務代碼中,我修改了代碼的解組部分以使用Struct對象。此代碼依賴于mapstructure包:


func getData[T ~proto.Message](data []byte) ([]T, error) {


    var resp *ArrayRespone

    if err := json.Unmarshal(data, &resp); err != nil {

        return nil, err

    }

    

    items := make([]T, len(resp.Results))

    for i, result := range resp.Results {

        var item T

        if err := mapstructure.Decode(result.AsMap(), &item); err != nil {

            return nil, err

        }


        items[i] = item

    }


    return items, nil

}

只要您的所有字段都可以輕松反序列化為google.protobuf.Value類型上的字段,這就可以工作。但是,對我來說情況并非如此,因為我調用的類型中的幾個字段getData具有UnmarshalJSON. 所以,我實際選擇的解決方案是改為使用bytes:


使用字節(jié)

對于這個實現(xiàn),我不需要依賴任何導入的類型,所以消息本身更容易處理:


message ArrayRespone {

    int32 count = 1;

    string next_url = 2;

    string request_id = 3;

    bytes results = 4;

    string status = 5;

}

這仍然需要為 開發(fā)自定義實現(xiàn)UnmarshalJSON,但該實現(xiàn)也更簡單:


func (resp *ArrayRespone) UnmarshalJSON(data []byte) error {


    // First, deserialize the JSON into a mapping between key fields and values

    // If this fails then return an error

    var mapped map[string]*json.RawMessage

    if err := json.Unmarshal(data, &mapped); err != nil {

        return fmt.Errorf("failed to perform first-pass unmarshal, error: %v", err)

    }


    // Next, extract the count from the mapping; if this fails return an error

    if err := extractValue(mapped, "count", &resp.Count); err != nil {

        return err

    }


    // Extract the next URL from the mapping; if this fails return an error

    if err := extractValue(mapped, "next_url", &resp.NextUrl); err != nil {

        return err

    }


    // Extract the request ID from the mapping; if this fails return an error

    if err := extractValue(mapped, "request_id", &resp.RequestId); err != nil {

        return err

    }


    // Extract the status from the mapping; if this fails return an error

    if err := extractValue(mapped, "status", &resp.Status); err != nil {

        return err

    }


    // Finally, iterate over each result and add it to the slice of results by attempting

    // to convert it to a Struct; if any of these fail to convert then return an error

    if raw, ok := mapped["results"]; ok {

        resp.Results = *raw

    }


    return nil

}


// Helper function that attempts to extract a value from a standard mapping of interfaces

// and set a field with it if the types are compatible

func extractValue[T any](mapping map[string]*json.RawMessage, field string, value *T) error {

    if raw, ok := mapping[field]; ok {

        if err := json.Unmarshal(*raw, &value); err != nil {

            return fmt.Errorf("failed to set value %s to field %s (%T)", *raw, field, *value)

        }

    }


    return nil

}

然后,我getData將函數(shù)修改為:


func getData[T ~proto.Message](data []byte) ([]T, error) {


    var resp *ArrayRespone

    if err := json.Unmarshal(data, &resp); err != nil {

        return nil, err

    }

    

    var items []T

    if err := json.Unmarshal(resp.Results, &items); err != nil {

        return nil, err

    }


    return items, nil

}

顯然,這個實現(xiàn)更簡單,需要的反序列化步驟更少,這意味著比Struct實現(xiàn)更少的反射。


查看完整回答
反對 回復 2022-12-19
  • 1 回答
  • 0 關注
  • 259 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網(wǎng)微信公眾號