Search code examples
jsongostack-overflowunmarshalling

implementing Unmarshaller without using a temp struct


There is an Unmarshaller implementation down below because of time.Unix does only accepts seconds or nanoseconds but my data source is in milliseconds. Before asking my questions here is some code

Code:

type Platform struct {
    Status                CampaignStatus `json:"status" bson:"status"`
    TotalBudget           int            `json:"total_budget" bson:"totalBudget"`
    RemainingBudget       int            `json:"remaining_budget" bson:"remainingBudget"`
    MillisecondsStartDate int64          `json:"start_date"`
    StartDate             time.Time      `bson:"startDate"`
    MillisecondsEndDate   int64          `json:"end_date"`
    EndDate               time.Time      `bson:"endDate"`
    Audiance              Audiance       `json:"target_audiance" bson:"targetAudiance"` //typo?
    Creatives             Creatives      `json:"creatives" bson:"Creatives"`
    Insights              Insights       `json:"insights" bson:"insights"`
}

func (p *Platform) UnmarshalJSON(b []byte) (err error) {

    p2 := struct {
        Status                CampaignStatus `json:"status" bson:"status"`
        TotalBudget           int            `json:"total_budget" bson:"totalBudget"`
        RemainingBudget       int            `json:"remaining_budget" bson:"remainingBudget"`
        MillisecondsStartDate int64          `json:"start_date"`
        StartDate             time.Time      `bson:"startDate"`
        MillisecondsEndDate   int64          `json:"end_date"`
        EndDate               time.Time      `bson:"endDate"`
        Audiance              Audiance       `json:"target_audiance" bson:"targetAudiance"` //typo?
        Creatives             Creatives      `json:"creatives" bson:"Creatives"`
        Insights              Insights       `json:"insights" bson:"insights"`
    }{}

    err = json.Unmarshal(b, &p2)
    if err != nil {
        return
    }

    p2.StartDate = time.Unix(0, p2.MillisecondsStartDate*int64(time.Millisecond/time.Nanosecond))
    p2.EndDate = time.Unix(0, p2.MillisecondsEndDate*int64(time.Nanosecond/time.Microsecond))

    *p = p2

    return
}

My questions are

  1. Is there a way to implement Unmarshaller without creating a middle struct in this scenario? As you may see if I use the Platform type isntead of middle struct I get stack overflow error.
  2. Could this approach ends up with memory leaks?

Solution

  • You could compose the fields that the marshaller can handle as an anonymous struct inside the Platform struct, something like:

    type platformInner struct {
        Status                CampaignStatus `json:"status" bson:"status"`
        TotalBudget           int            `json:"total_budget" bson:"totalBudget"`
        RemainingBudget       int            `json:"remaining_budget" bson:"remainingBudget"`
        MillisecondsStartDate int64          `json:"start_date"`
    
        MillisecondsEndDate   int64          `json:"end_date"`
    
        Audiance              Audiance       `json:"target_audiance" bson:"targetAudiance"` //typo?
        Creatives             Creatives      `json:"creatives" bson:"Creatives"`
        Insights              Insights       `json:"insights" bson:"insights"`
    }
    
    type Platform struct {
        platformInner
        StartDate             time.Time      `bson:"startDate"`
        EndDate               time.Time      `bson:"endDate"`
    }
    

    And then in the custom unmarshaller, unmarshal into the nested struct, and set the other values.

    func (p *Platform) UnmarshalJSON(b []byte) (err error) {
        var inner platformInner
        err = json.Unmarshal(b, &inner)
        if err != nil {
            return
        }
    
        tmp := &Platform{
            innerPlatform: inner,
            StartDate:     time.Unix(0, inner.MillisecondsStartDate*int64(time.Millisecond/time.Nanosecond))
            EndDate:       time.Unix(0, inner.MillisecondsEndDate*int64(time.Nanosecond/time.Microsecond))
        }
    
        *p = tmp
    
        return
    }