I have jsonb value stored in postgres columns which I need to convert to object to send the response
I am unable to convert JSON -> struct using json.Unmarshal method
type Result struct {
Id int
Expertise string // because json string
Languages string // because json string
}
// saving dd query data to result struct
e, _ := json.Marshal(data.Expertise)
l, _ := json.Marshal(data.Languages)
row := r.db.QueryRow(`
INSERT INTO filters (user_id, expertise, languages)
VALUES ($1, $2, $3)
ON CONFLICT (user_id) DO
UPDATE SET expertise=$2, languages=$3
RETURNING id, expertise, languages;
`, userid, e, l)
var res Result
err := row.Scan(&res.Id, &res.Expertise, &res.Languages)
Then I take this the Expertise and Languages field and UNMARSHAL THEM
// helper method
func Unmarshal(value string) interface{} {
var obj interface{}
err := json.Unmarshal([]byte(value), &obj)
return obj
}
type CreateFilterResponse struct {
Id int `json:"id"`
Expertise interface{} `json:"expertise"`
Languages interface{} `json:"languages"`
}
response := dto.CreateFilterResponse{
Id: res.Id,
Expertise: Unmarshal(res.Expertise), // 👈 not decoding, still json
Languages: Unmarshal(res.Languages), // 👈 not decoding, still json
}
I would really appreciate the help, I need the Expertise and Languages to be {} and [] respectively
This is what I am getting in response:
{"id":5,"expertise":"[{\"role\":1,\"experience\":5},
{\"role\":3,\"experience\":4}]","languages":"[1,2,3]"}
Note:
Expertise is jsonb column : []{role: int, experience: int}
Languages is jsonb column : []int
Thanks to #Blackgreen comment, the suggestion worked.
As he suggested "I believe the issue is that you are scanning the jsonb bytes into string, which causes it to be interpreted literally. Then when you pass a string into json.Unmarshal it stays a string. Change the type of Expertise and Language to []byte (or json.RawMessage)"
I did the same and it works, here is the code:
// for parsing jsonb coming from db query
type Result struct {
Id int
Expertise json.RawMessage // change
Languages json.RawMessage // change
}
// the database query itself
e, _ := json.Marshal(filters.Expertise)
l, _ := json.Marshal(filters.Languages)
row := r.db.QueryRow(`
INSERT INTO filters (user_id, expertise, languages)
VALUES ($1, $2, $3)
ON CONFLICT (user_id) DO
UPDATE SET expertise=$2, languages=$3
RETURNING id, expertise, languages;
`, userid, e, l)
var res Result
err := row.Scan(&res.Id, &res.Expertise, &res.Languages) // scanning to result
if err != nil {
fmt.Println("Unable to create a new filter ==>", err)
return nil, err
}
Then I Unmarshalled the expertise and languages jsonb values using a helper method and created a Response struct for the client:
// response struct type
type CreateFilterResponse struct {
Id int `json:"id"`
Expertise interface{} `json:"expertise"`
Languages interface{} `json:"languages"`
}
// final response struct
response := dto.CreateFilterResponse{
Id: res.Id,
Expertise: Unmarshal(res.Expertise),
Languages: Unmarshal(res.Languages),
}
// helper method
func Unmarshal(value []byte) interface{} {
var obj interface{}
json.Unmarshal(value, &obj)
return obj
}
This works for now. I still need to find an easy way to do this as this is too much boilerplate code to perform a simple task. like in JS/TS it can be done using single line of code JSON.parse(value)
The problem is in the json output. It is encoding your JSON twice, making it difficult to read and deserialize the json in your structure.
You can start by organizing your structures as follows:
type Expertise struct {
Role int `json:"role"`
Experience int `json:"Experience"`
}
type CreateFilterResponse struct {
Id int `json:"id"`
Expert []Expertise `json:"expertise"`
Languages []int `json:"languages"`
}
I made a cleaner method just to exemplify and facilitate understanding. It will remove the unnecessary quotes and escapes so you can convert your string to []bytes and unmarshal your structure.
func jsonCleaner(quoted string) []byte{
quoted = strings.ReplaceAll(quoted, "\n", "")
quoted = strings.ReplaceAll(quoted, "\\", "")
quoted = strings.ReplaceAll(quoted, "]\"", "]")
quoted = strings.ReplaceAll(quoted, "\"[", "[")
dataBytes := []byte(quoted)
fmt.Println(dataBytes)
return dataBytes
}
Your json message would go from:
{"id":5,"expertise":"[{\"role\":1,\"experience\":5},{\"role\":3,\"experience\":4}]","languages":"[1,2,3]"}
To:
{"id":5,"expertise":[{"role":1,"experience":5},"role":3,"experience":4}],"languages":[1,2,3]}
I'm looking to filter an array of arrays by specific value of one of the keys located within each array. Each nested array is read in from Firestore.
As an object, each nested array would look like this:
struct Video {
var url: String
var submissionDate: Timestamp
var submittingUser: String
}
I'm reading it in like this:
videos = document.get("videos") as? [[String : Any]] ?? nil
So far so good, but when I filter it like this:
filteredVideos = videos.filter { $0[2].contains(self.userIdentification) }
I can't do it without getting the error "Reference to member 'contains' cannot be resolved without a contextual type," an error which I was unable to find any relevant information on SO about.
I have read that some people say "Don't use arrays in Firestore!" but this is a build requirement.
Anyone have any ideas? Basically just need all arrays within the array where userId == submittingUser.
Reference Article:
I tried the answer from here: How to filter out a Array of Array's but no luck for this situation.
It's actually an array of dictionaries, not an array of arrays. All you need to do is construct the right predicate for the filter.
This is basically what your Firestore data looks like:
let videos: [[String: Any]] = [
["url": "http://www...", "submittingUser": "user1"],
["url": "http://www...", "submittingUser": "user2"]
]
This is the user you're looking for:
let userIdentification = "user2"
This is the predicate for the filer:
let filteredVideos = videos.filter { (video) -> Bool in
if let submittingUser = video["submittingUser"] as? String,
submittingUser == userIdentification {
return true
} else {
return false
}
}
You can shorthand this down to a single line if you're okay with force-unwrapping the dictionary (if you're 100% certain every video will have a valid submittingUser value):
let filteredVideos = videos.filter({ $0["submittingUser"] as! String == userIdentification })
I am trying to insert an array into a MongoDB instance using Go. I have the [] string slice in Go and want to convert it into a BSON array to pass it to the DB using the github.com/mongodb/mongo-go-driver driver.
var result bson.Array
for _, data := range myData {
value := bson.VC.String(data)
result.Append(value)
}
This loops over each element of my input data and tries to append it to the BSON array. However the line with the Append() fails with panic: document is nil. How should I do this conversion?
Edit: The code in the question and this answer is no longer relevant because the bson.Array type was deleted from the package. At the time of this edit, the bson.A and basic slice operations should be used to construct arrays.
Use the factory function NewArray to create the array:
result := bson.NewArray()
for _, data := range myData {
value := bson.VC.String(data)
result.Append(value)
}
As mentioned by #Cerise bson.Array has since been deleted. I do this with multiple utility functions as follows:
func BSONStringA(sa []string) (result bson.A) {
result = bson.A{}
for_, e := range sa {
result = append(result, e)
}
return
}
func BSONIntA(ia []string) (result bson.A) {
// ...
}
Converting a slice of string (ids) to BSON array
var objIds bson.A
for _, val := range ids {
objIds = append(objIds, val)
}
log.Println(objIds)
I am trying to insert multiple values in a Set. So, I did this
var skuSet: Set = Set<String>()
{
(response, error) in
if (error != nil)
{
print("Error \(error.debugDescription)")
}
else
{
if let result = response.arrayObject as? Array<Dictionary<String, Any>>
{
self.coinsArr = result
let multiple = result.compactMap{ $0["sku"] as? String }
self.skuSet.insert(multiple)
}
}
But, the insert line throws the error
Cannot convert value of type '[String]' to expected argument type 'String'.
Any help?
insert is for a single item. Use formUnion to insert multiple items from a Sequence into a Set
self.skuSet.formUnion(multiple)
And if you want to replace the contents of the set with the multiple array simply write
self.skuSet = Set(multiple)
The documentation for compactMap(_:) says:
compactMap(_:) returns an array containing the non-nil results of
calling the given transformation with each element of this sequence.
However, the documentation for set insert(_:) also says:
insert(_:) inserts the given element in the set if it is not already
present.
You assign an array of strings to multiple, and expect to insert this array using insert(_:) but it's not possible.
Add the below function to Set extension in order to insert multiple elements:
func insert(_ elements: [Set.Element]) {
elements.forEach { (element) in
self.insert(element)
}
}
Alternatively, use formUnion(_:):
self.skuSet.formUnion(multiple)
...or use its immutable version, union(_:):
var skuSetUnionMultiple = self.skuSet.union(multiple)
I have a GOLANG struct as follows:
type OrgWhoAmI struct {
FriendlyName string `json:"friendlyName"`
RedemptionCode string `json:"redemptionCode"`
StartUrls []StartUrl `json:"startUrls"`
Status string `json:"status"`
Children []OrgChildren `json:"childrenReemptionCodes"`
}
type StartUrl struct {
DisplayName string `json:"displayName"`
URL string `json:"url"`
}
type OrgChildren struct {
FriendlyName string `json:"childFriendlyName"`
RedemptionCode string `json:"childRedemptionCode"`
}
I've created and successfully inserted records into a MongoDB collection (as I can see the results by querying Mongo with the CLI mongo program) - but when I query with MGO as follows, I get nothing:
func main() {
session, sessionErr := mgo.Dial("localhost")
defer session.Close()
// Query All
collection := session.DB("OrgData").C("orgWhoAmI")
var results []OrgWhoAmI
err = collection.Find(bson.M{}).All(&results)
if err != nil {
panic(err)
}
for _, res := range results {
fmt.Printf("Result: %s|%s\n", res.FriendlyName, res.RedemptionCode)
}
}
The results printed are:
Result: |
Result: |
Result: |
Result: |
If I ask for the count for records, I get the correct number, but all values for all fields are blank. Not sure what I'm missing here.
If you aren't creating them in go, it's probably not serializing the key names for you properly. The default for bson is to lowercase the keys, so you need to specify it if you want something else. Also note that you have a typo in OrgWhoAmI for json:"childrenReemptionCodes" (should be Redemption, I'm guessing). You can specify both bson and json separately if you want them to be different.
type OrgWhoAmI struct {
FriendlyName string `bson:"friendlyName" json:"friendlyName"`
RedemptionCode string `bson:"redemptionCode" json:"redemptionCode"`
StartUrls []StartUrl `bson:"startUrls" json:"startUrls"`
Status string `bson:"status" json:"status"`
Children []OrgChildren `bson:"childrenRedemptionCodes" json:"childrenRedemptionCodes"`
}
type StartUrl struct {
DisplayName string `bson:"displayName" json:"displayName"`
URL string `bson:"url" json:"url"`
}
type OrgChildren struct {
FriendlyName string `bson:"childFriendlyName" json:"childFriendlyName"`
RedemptionCode string `bson:"childRedemptionCode" json:"childRedemptionCode"`
}