How to filter JSON data based off an empty field?

Issue

I’m calling an API and trying to parse the body response to filter out data. I’ve created a conditional for-loop to go through each object and check whether this field: Relationships.CurrentConfigurationVersion.Data is empty.

Here is my first attempt:

func (s *Server) getEmptyWorkSpaces(w http.ResponseWriter, r *http.Request) {
    
    // omitted API calls for brevity

    // body holds the JSON response
    body, err := ioutil.ReadAll(resp.Body)

    // label my struct type as data
    var data WorkspacesJSON

    err = json.Unmarshal(body, data)
    if err != nil {
        panic(err)
    }

    var data2 []*WorkspacesJSON
    for _, v := range data.Data {
        if v.Relationships.CurrentConfigurationVersion.Data == " " {
            data2 = append(data2, v)
        }
    }
}

The error occurs in the conditional statement because I’m comparing a struct type to a string.

cannot convert "" (untyped string constant) to struct{ID string "json:"id""; Type string "json:"type""}


Attempt 2
After some searching around I tried another attempt from information I learned:

func (s *Server) getEmptyWorkSpaces(w http.ResponseWriter, r *http.Request) {
    
    // omitted API calls for brevity

    // body holds the JSON response
    body, err := ioutil.ReadAll(resp.Body)

    // label my struct type as data, this time attach []*
    var data []*WorkspacesJSON

    // added & in this new attempt
    err = json.Unmarshal(body, &data)
    if err != nil {
        panic(err)
    }

    var data2 []*WorkspacesJSON
    for _, v := range data.Data {
        if v.Relationships.CurrentConfigurationVersion.Data == " " {
            data2 = append(data2, v)
        }
    }
}

The compiler throw another error but this time targeting v.Relationships.CurrentConfigurationVersion.Data saying:

v.Relationships undefined (type *WorkspacesJSON has no field or method Relationships)compilerMissingFieldOrMethod
I’m not sure why I’m getting this error because in my first attempt this was not a problem? Clearly I am not understanding this.

Here is my type struct:
It’s fairly long but the only field of important is Data.Relationships.CurrentConfigurationVersion.Data

type WorkspacesJSON struct {
    Data []struct {
        ID         string `json:"id"`
        Type       string `json:"type"`
        Attributes struct {
            AllowDestroyPlan           bool        `json:"allow-destroy-plan"`
            AutoApply                  bool        `json:"auto-apply"`
            AutoDestroyAt              interface{} `json:"auto-destroy-at"`
            CreatedAt                  time.Time   `json:"created-at"`
            Environment                string      `json:"environment"`
            Locked                     bool        `json:"locked"`
            Name                       string      `json:"name"`
            QueueAllRuns               bool        `json:"queue-all-runs"`
            SpeculativeEnabled         bool        `json:"speculative-enabled"`
            StructuredRunOutputEnabled bool        `json:"structured-run-output-enabled"`
            TerraformVersion           string      `json:"terraform-version"`
            WorkingDirectory           string      `json:"working-directory"`
            GlobalRemoteState          bool        `json:"global-remote-state"`
            UpdatedAt                  time.Time   `json:"updated-at"`
            ResourceCount              int         `json:"resource-count"`
            ApplyDurationAverage       int         `json:"apply-duration-average"`
            PlanDurationAverage        int         `json:"plan-duration-average"`
            PolicyCheckFailures        int         `json:"policy-check-failures"`
            RunFailures                int         `json:"run-failures"`
            WorkspaceKpisRunsCount     int         `json:"workspace-kpis-runs-count"`
            LatestChangeAt             time.Time   `json:"latest-change-at"`
            Operations                 bool        `json:"operations"`
            ExecutionMode              string      `json:"execution-mode"`
            VcsRepo                    struct {
                Branch            string `json:"branch"`
                IngressSubmodules bool   `json:"ingress-submodules"`
                Identifier        string `json:"identifier"`
                DisplayIdentifier string `json:"display-identifier"`
                OauthTokenID      string `json:"oauth-token-id"`
                WebhookURL        string `json:"webhook-url"`
                RepositoryHTTPURL string `json:"repository-http-url"`
                ServiceProvider   string `json:"service-provider"`
            } `json:"vcs-repo"`
            VcsRepoIdentifier string `json:"vcs-repo-identifier"`
            Permissions       struct {
                CanUpdate              bool `json:"can-update"`
                CanDestroy             bool `json:"can-destroy"`
                CanQueueDestroy        bool `json:"can-queue-destroy"`
                CanQueueRun            bool `json:"can-queue-run"`
                CanQueueApply          bool `json:"can-queue-apply"`
                CanReadStateVersions   bool `json:"can-read-state-versions"`
                CanCreateStateVersions bool `json:"can-create-state-versions"`
                CanReadVariable        bool `json:"can-read-variable"`
                CanUpdateVariable      bool `json:"can-update-variable"`
                CanLock                bool `json:"can-lock"`
                CanUnlock              bool `json:"can-unlock"`
                CanForceUnlock         bool `json:"can-force-unlock"`
                CanReadSettings        bool `json:"can-read-settings"`
                CanManageTags          bool `json:"can-manage-tags"`
            } `json:"permissions"`
            Actions struct {
                IsDestroyable bool `json:"is-destroyable"`
            } `json:"actions"`
            Description         interface{}   `json:"description"`
            FileTriggersEnabled bool          `json:"file-triggers-enabled"`
            TriggerPrefixes     []interface{} `json:"trigger-prefixes"`
            Source              string        `json:"source"`
            SourceName          interface{}   `json:"source-name"`
            SourceURL           interface{}   `json:"source-url"`
            TagNames            []interface{} `json:"tag-names"`
        } `json:"attributes"`
        Relationships struct {
            Organization struct {
                Data struct {
                    ID   string `json:"id"`
                    Type string `json:"type"`
                } `json:"data"`
            } `json:"organization"`
            CurrentRun struct {
                Data struct {
                    ID   string `json:"id"`
                    Type string `json:"type"`
                } `json:"data"`
                Links struct {
                    Related string `json:"related"`
                } `json:"links"`
            } `json:"current-run"`
            LatestRun struct {
                Data struct {
                    ID   string `json:"id"`
                    Type string `json:"type"`
                } `json:"data"`
                Links struct {
                    Related string `json:"related"`
                } `json:"links"`
            } `json:"latest-run"`
            Outputs struct {
                Data []interface{} `json:"data"`
            } `json:"outputs"`
            RemoteStateConsumers struct {
                Links struct {
                    Related string `json:"related"`
                } `json:"links"`
            } `json:"remote-state-consumers"`
            CurrentStateVersion struct {
                Data struct {
                    ID   string `json:"id"`
                    Type string `json:"type"`
                } `json:"data"`
                Links struct {
                    Related string `json:"related"`
                } `json:"links"`
            } `json:"current-state-version"`
            CurrentConfigurationVersion struct {
                Data struct {
                    ID   string `json:"id"`
                    Type string `json:"type"`
                } `json:"data"`
                Links struct {
                    Related string `json:"related"`
                } `json:"links"`
            } `json:"current-configuration-version"`
            AgentPool struct {
                Data interface{} `json:"data"`
            } `json:"agent-pool"`
            Readme struct {
                Data struct {
                    ID   string `json:"id"`
                    Type string `json:"type"`
                } `json:"data"`
            } `json:"readme"`
        } `json:"relationships"`
        Links struct {
            Self string `json:"self"`
        } `json:"links"`
    } `json:"data"`
    Links struct {
        Self  string      `json:"self"`
        First string      `json:"first"`
        Prev  interface{} `json:"prev"`
        Next  string      `json:"next"`
        Last  string      `json:"last"`
    } `json:"links"`
    Meta struct {
        StatusCounts struct {
            Pending            int `json:"pending"`
            PlanQueued         int `json:"plan-queued"`
            Planning           int `json:"planning"`
            Planned            int `json:"planned"`
            Confirmed          int `json:"confirmed"`
            ApplyQueued        int `json:"apply-queued"`
            Applying           int `json:"applying"`
            Applied            int `json:"applied"`
            Discarded          int `json:"discarded"`
            Errored            int `json:"errored"`
            Canceled           int `json:"canceled"`
            CostEstimating     int `json:"cost-estimating"`
            CostEstimated      int `json:"cost-estimated"`
            PolicyChecking     int `json:"policy-checking"`
            PolicyOverride     int `json:"policy-override"`
            PolicyChecked      int `json:"policy-checked"`
            PolicySoftFailed   int `json:"policy-soft-failed"`
            PlannedAndFinished int `json:"planned-and-finished"`
            PostPlanRunning    int `json:"post-plan-running"`
            PostPlanCompleted  int `json:"post-plan-completed"`
            PreApplyRunning    int `json:"pre-apply-running"`
            PreApplyCompleted  int `json:"pre-apply-completed"`
            Fetching           int `json:"fetching"`
            None               int `json:"none"`
            Total              int `json:"total"`
        } `json:"status-counts"`
        Pagination struct {
            CurrentPage int         `json:"current-page"`
            PageSize    int         `json:"page-size"`
            PrevPage    interface{} `json:"prev-page"`
            NextPage    int         `json:"next-page"`
            TotalPages  int         `json:"total-pages"`
            TotalCount  int         `json:"total-count"`
        } `json:"pagination"`
    } `json:"meta"`
}

I’m stuck in an eternal loop of those two same errors above, and trying to create hacky functions to get the job done, but no luck.

I’m very new to Go, I have experience with Python and working with JSON in Py is much easier for me but I want to try this using Go. Thanks in advance for your guidance.

How can I filter my JSON body using a conditional to check if a field is empty?

EDIT:
In my second attempt, How would I be able to select the field I’m looking for, Relationships.CurrentConfigurationVersion.Data? Once I figure that out I think I will be okay. Currently its saying that WorkspaceJSON has no field of method forRelationships. This was not the case for my first attempt.

Solution

There are lots of ways to check is struct empty or not, which is discussed here: How to check for an empty struct?

Also the append part of the code, must have the same type; as the following code:

data2 := WorkspacesJSON{}
for _, v := range data.Data {
    if fmt.Sprintf("%v", v.Relationships.CurrentConfigurationVersion.Data) == "{ }" {
        data2.Data = append(data2.Data, v)
    }
}

Answered By – Amirreza Noori

Answer Checked By – Clifford M. (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.