在不使用临时结构的情况下实现Unmarshaller

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?

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
}

Instead of duplicating the struct definition for the anonymous "p2" struct type you could alias it under a new name, such as "PlatformTemp" like so:

func (p *Platform) UnmarshalJSON(b []byte) (err error) {
  type PlatformTemp Platform
  var p2 PlatformTemp

  err = json.Unmarshal(b, &p2)
  // ...

  *p = Platform(p2)
  return nil
}

Since the types "Platform" and "PlatformTemp" have the same fields they can be assigned to each other with a simple cast; however, since "PlatformTemp" has no "UnmarshalJSON" method defined it will use the default unmarshaler.