S
S
SignOfChaos2018-10-19 07:46:51
go
SignOfChaos, 2018-10-19 07:46:51

A structure with a field containing a JSON string. How to get the correct JSON from such a structure?

Good afternoon!
there is a structure

type DetailedReportRow struct {
  Id           int64      `json:"id"`
  Dt           *time.Time `json:"dt"`
  Phone        string     `json:"phone"`
  TicketSums   string	  `json:"tickets" sql:"type:json"`
  PaymentSum   float32    `json:"payment_sum"`
  PaySystemId  float32    `json:"pay_system_id"`
  PayStateName string     `json:"pay_state_name"`
  BusNo		string	  `json:"bus_no"`
}

there is a query to the PostgreSQL database, the result of which fills an array of instances of the specified structure
...
var result []models.DetailedReportRow
...
slq := fmt.Sprintf(`
    select 
      p.id,
      p.success_dt as dt,
      p.phone,
      p.payment_sum,
      p.pay_system_id,
      ps.name as pay_state_name,
      bb.state_number as bus_no,
      p.json_data_r::json->>'s' as ticket_sums -- элемент s содержит массив простых объектов вида [{"p": 90}, {"p": 90}]
    FROM 
      pay_payment p
      LEFT JOIN pay_state ps on	ps.id = p.pay_state_id
      LEFT JOIN bus_bus bb on (bb.qr = p.json_data_r::json->>'q')
    ...
`)

  controllers.DB.Raw(slq).Scan(&result)
  return result

the ticket_sums field, of course, is returned as a string.
then the result is returned to the client from the Revel controller:
func (c ManagerAuthApiCtl) BusDetailedReport() revel.Result {
  ...
  stat := repository.Bus().DetailedReport(date, system, fleet)
  return c.RenderJSON(JsonResponse{
    Success: true,
    Data:    map[string]interface{}{"stat": stat},
  })
}

The client receives json like this:
{
  "success": true,
  "data": {
    "stat": [
      {
        "id": 185247,
        "dt": "2018-10-18T18:36:07Z",
        "phone": "0000000000",
        "tickets": "[{\"p\": 90}, {\"p\": 90}]",
        "payment_sum": 180,
        "pay_system_id": 1,
        "pay_state_name": "Успешно оплачен",
        "bus_no": "157 АТ 01"
      },
      ...
    ]
  }
}

it needs to
"tickets": "[{\"p\": 90}, {\"p\": 90}]"
be:
"tickets": [{"p": 90}, {"p": 90}]
i.e. as an array of objects, not a string with escaped quotes
, searches on the Internet led to the fact that the field of the structure containing json should be declared like this:
type DetailedReportRaw struct {
  ...
  TicketSums	 json.RawMessage		`json:"tickets"`
  ...
}

, but at the same time in the issued json field tickets is equal to nil
Tell me, who came across, how to get the desired result?
Thank you!

Answer the question

In order to leave comments, you need to log in

3 answer(s)
A
Alexander Pavlyuk, 2018-10-19
@pav5000

You just need to make the tickets field a string and unmarshal directly from it, casting it to []byte.

result := map[string]int{}
json.Unmarshal([]byte(someStruct.tickets), &result)

A
Abcdefgk, 2018-10-21
@Abcdefgk

It is necessary to throw out these slashes from the string on the client and that's all - str.replace(/\\/g, '');
"tickets": "[{"p": 90}, {"p": 90}]",

E
Evgeny, 2018-10-22
@bat

web searches resulted in json.RawMessage

the direction is right
it is possible that controllers.DB.Raw(slq).Scan(&result) can't work with this type
try what happens if you declare a field how will there be data in it, if yes, then you can dig further to unmarshal the line, as suggested above, You can, if you need to quickly fix. As a solution - a crutch, an unnecessary overhead for decoding / encoding a ready-made json.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question