I'm trying to send a JSON payload in my POST request but I'm not sure on how to format it correctly to use arrays. This below is what the correct JSON itself looks like:
{
"animal": "dog",
"contents": [{
"name": "daisy",
"VAL": "234.92133",
"age": 3
}]
}
I have this so far:
group := map[string]interface{}{
"animal": "dog",
"contents": map[string]interface{}{
"name": "daisy",
"VAL": "234.92133",
"age": 3,
},
}
But I can't figure out how to do array of contents (the square brackets), only the curly brackets from "contents".
The quick answer:
group := map[string]interface{}{
"animal": "dog",
"contents": []map[string]interface{}{
{
"name": "daisy",
"VAL": "234.92133",
"age": 3,
},
},
}
But as already said in the comments it is better (type safety) to use structs instead:
type Animal struct {
Type string `json:"animal"`
Contents []AnimalContent `json:"contents"`
}
type AnimalContent struct {
Name string `json:"name"`
Value string `json:"VAL"`
Age int `json:"age"`
}
Then create with:
group := Animal{
Type: "dog",
Contents: []AnimalContent{
{
Name: "daisy",
Value: "234.92133",
Age: 3,
},
},
}
// to transform to json format
bts, err := json.Marshal(group)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(bts))
Related
sample json data is this:
{
"variations": [
{
"variation_id": 391,
"name": "Fruit Shake - Chocolate, S",
"price": 10,
"attribute": [
{
"attribute_key": "pa_flavor",
"name": "Flavor",
"option": "Chocolate"
},
{
"attribute_key": "pa_size",
"name": "Size",
"option": "S"
}
]
},
{
"variation_id": 385,
"name": "Fruit Shake - Banana, L",
"price": 18,
"attribute": [
{
"attribute_key": "pa_flavor",
"name": "Flavor",
"option": "Banana"
},
{
"attribute_key": "pa_size",
"name": "Size",
"option": "L"
}
]
},
{
"variation_id": 386,
"name": "Fruit Shake - Banana, M",
"price": 15,
"attribute": [
{
"attribute_key": "pa_flavor",
"name": "Flavor",
"option": "Banana"
},
{
"attribute_key": "pa_size",
"name": "Size",
"option": "M"
}
]
}
]
}
my problem is, getting the variation_id where 2 or more attributes matches the array of string.
for example, chosenProduct = ["Banana", "L"]
I tried filter and contains but theres no way to match the other item from chosenProduct.
If I added the next condition, it returns nil
You can try this:
let varID = product.variations?.filter { att in
var matchedAttributes = 0
for thisAttribute in att.attribute {
if chosenProduct.contains(where: {$0 == thisAttribute.option}) {
matchedAttributes += 1
}
}
if matchedAttributes >= 2 {
return true
}
return false
}
Let me know if there's any doubt.
You can try this :
var chosenProduct = ["Banana","L"]
var expectedIds : [Int] = [Int]()
datas.variations?.map{ val in
val.attribute.map({ val2 in
let filtered = val2.enumerated().filter({chosenProduct.contains($0.element.option!)})
if filtered.count == chosenProduct.count{
expectedIds.append(val.variation_id!)
}
})
}
print(expectedIds) // [385]
I put the id's in array because of if you want to change your chosenProcudt example "Banana" (count 1 ). This mapping must be return variation_id like [385,386]
You can a method to Variation to make things easier:
extension Variation {
func attributesMatching(options: [String]) -> [Attribute] {
attribute.filter { options.contains($0.option) }
}
}
Then, you can just write:
let filtered = product.variations.filter { $0.attributesMatching(options: chosenProductOptions).count >= 2 }
print(filtered.map { $0.variationID })
I'm just learning the Swift Decodable protocol and am running into a problem. I am able to decode one json object into a swift object, but am stuck with decoding an array.
What goes well:
imagine following json:
let json = """
{
"all" : {
"_id": "59951d5ef2db18002031693c",
"text": "America’s cats, including housecats that adventure outdoors and feral cats, kill between 1.3 billion and 4.0 billion birds in a year.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
}
}
"""
let jsonData = json.data(using: .utf8)
I can decode it to a Fact object with following code:
enum Type: String, Decodable {
case cat = "cat"
}
struct Fact {
let id: String
let text: String
let type: Type
let upvotes: Int
enum CodingKeys: CodingKey {
case all
}
enum FactKeys: CodingKey {
case _id, text, type, upvotes
}
}
extension Fact: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let allContainer = try container.nestedContainer(keyedBy: FactKeys.self, forKey: .all)
id = try allContainer.decode(String.self, forKey: ._id)
text = try allContainer.decode(String.self, forKey: .text)
type = try allContainer.decode(Type.self, forKey: .type)
upvotes = try allContainer.decode(Int.self, forKey: .upvotes)
}
}
let decoder = JSONDecoder()
let fact = try decoder.decode(Fact.self, from: jsonData!)
But the api is giving me an array of objects:
let json = """
{
"all": [
{
"_id": "59951d5ef2db18002031693c",
"text": "America’s cats, including housecats that adventure outdoors and feral cats, kill between 1.3 billion and 4.0 billion birds in a year.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
},
{
"_id": "5b01a447c6914f0014cc9a30",
"text": "The special sensory organ called the Jacobson's organ allows a cat to have 14 times the sense of smell of a human. It consists of two fluid-filled sacs that connect to the cat's nasal cavity and is located on the roof of their mouth behind their teeth.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
}
]
}
"""
let jsonData = json.data(using: .utf8)
And I want to store that in an allFacts array that hold my Fact objects
class Facts: ObservableObject {
#Published var allFacts = [Fact]()
}
let decoder = JSONDecoder()
let allFacts = try decoder.decode([Fact].self, from: jsonData!)
I'm using the same extension on my Fact struct. But it's giving me an error and I am totally lost for a second. Any idea how I can solve this ?
Do I need to create codingKeys for the class as well ?
Expected to decode Array<Any> but found a dictionary instead."
I recommend not to mess around with nested containers. This is less efficient than the default stuff. In your case you would have to use nestedUnkeyedContainer and iterate the array which is still more expensive.
Instead just add a root struct
let json = """
{
"all": [
{
"_id": "59951d5ef2db18002031693c",
"text": "America’s cats, including housecats that adventure outdoors and feral cats, kill between 1.3 billion and 4.0 billion birds in a year.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
},
{
"_id": "5b01a447c6914f0014cc9a30",
"text": "The special sensory organ called the Jacobson's organ allows a cat to have 14 times the sense of smell of a human. It consists of two fluid-filled sacs that connect to the cat's nasal cavity and is located on the roof of their mouth behind their teeth.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
}
]
}
"""
let jsonData = Data(json.utf8)
enum Type: String, Decodable {
case cat
}
struct Root : Decodable {
let all : [Fact]
}
struct Fact : Decodable {
let id: String
let text: String
let type: Type
let upvotes: Int
enum CodingKeys : String, CodingKey {
case id = "_id", text, type, upvotes
}
}
let decoder = JSONDecoder()
let root = try decoder.decode(Root.self, from: jsonData)
print(root.all)
I'm having trouble getting the direction values from the following JSON:
"routeOptions": [
{
"name": "Jubilee",
"directions": [
"Wembley Park Underground Station",
"Stanmore Underground Station"
],
"lineIdentifier": {
"id": "jubilee",
"name": "Jubilee",
"uri": "/Line/jubilee",
"type": "Line",
"routeType": "Unknown",
"status": "Unknown"
}
}
]
I believe the directions is a JSON array, which at the moment I'm using Codable as below. I've managed to get the routeOptions name but can't seem to figure out how to get the directions as there's no specific key variable. Please can someone help?
struct RouteOptions: Codable {
let name: String?
let directions: [Directions]?
init(name: String, directions: [Directions]) {
self.name = name
self.directions = directions
}}
struct Directions: Codable {}
You need to handle directions as an array of String
struct RouteOptions: Codable {
let name: String
let directions: [String]
}
Here is an example where I fixed the json to be correct
let data = """
{ "routeOptions": [
{
"name": "Jubilee",
"directions": [
"Wembley Park Underground Station",
"Stanmore Underground Station"
],
"lineIdentifier": {
"id": "jubilee",
"name": "Jubilee",
"uri": "/Line/jubilee",
"type": "Line",
"routeType": "Unknown",
"status": "Unknown"
}
}
]}
""".data(using: .utf8)!
struct Root: Decodable {
let routeOptions: [RouteOptions]
}
struct RouteOptions: Codable {
let name: String
let directions: [String]
}
do {
let result = try JSONDecoder().decode(Root.self, from: data)
print(result.routeOptions)
} catch {
print(error)
}
I have fetched data from a JSON file.. But when I tried to fetch another data from it, am unable to do so as it is a nested array... I know the solution can arrive easily but this is the first time am trying to loop a JSON file.. so kindly give your inputs.
SampleData = {
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true,
"members": [
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": [
"Immortality",
"Turning tiny",
"Radiation blast"
]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
},
{
"name": "Eternal Flame",
"age": 1000,
"secretIdentity": "Unknown",
"powers": [
"Immortality",
"Heat Immunity",
"Inferno",
"Teleportation",
"Interdimensional travel"
]
}
]
};
GetJsonData() {
console.log(this.SampleData["powers"]);
for (let i = 0; i < this.SampleData["powers"].length; i++) {
if (this.SampleData["powers"][i].Immortality) {
console.log(this.SampleData.powers[i]);
}
}
}
{name: "Molecule Man", age: 29, secretIdentity: "Dan Jukes", powers: Array(3)}
{name: "Eternal Flame", age: 1000, secretIdentity: "Unknown", powers: Array(3)}
Your code needs to follow the structure of the JSON data; in particular, these are all valid things you could print:
console.log(this.SampleData.squadName);
console.log(this.SampleData.homeTown);
console.log(this.SampleData.members[0].name);
console.log(this.SampleData.members[0].powers[0]);
If you wanted to loop through each member and print their info, that might look like this:
this.SampleData.members.forEach(member => {
let powerString = member.powers.join(', ');
console.log('Name: ' + member.name);
console.log('Age: ' + member.age);
console.log('Powers: ' + powerString);
});
I used a forEach, but you can also use a for (let i = loop.
I am trying to create an array from already created array. Array that I have is
{
"id": 1,
"category": "fruits",
"name": "Apple",
"description": "Apple is my favorite fruit."
}
{
"id": 2,
"category": "colors",
"name": "Red",
"description": "Red color is always charming."
}
{
"id": 3,
"category": "flowers",
"name": "Lotus",
"description": "It is one of the most beautiful flowers in this world."
}
{
"id": 4,
"category": "colors",
"name": "Pink",
"description": "A romantic color, mostly liked by women."
}
{
"id": 5,
"category": "flowers",
"name": "Rose",
"description": "I love roses."
}
{
"id": 6,
"category": "fruits",
"name": "Mango",
"description": "Mango is one of my favorite fruits."
}
Now I need to create an array and populate data like:
"elements":{
"fruits":{
0:{
"id": 1,
"category": "fruits",
"name": "Apple",
"description": "Apple is my favorite fruit."
}
1:{
"id": 6,
"category": "fruits",
"name": "Mango",
"description": "Mango is one of my favorite fruits."
}
}
"flowers":{
0:{
"id": 3,
"category": "flowers",
"name": "Lotus",
"description": "It is one of the most beautiful flowers in this world."
}
1:{
"id": 5,
"category": "flowers",
"name": "Rose",
"description": "I love roses."
}
}
"colors":{
0:{
"id": 2,
"category": "colors",
"name": "Red",
"description": "Red color is always charming."
}
1:{
"id": 4,
"category": "colors",
"name": "Pink",
"description": "A romantic color, mostly liked by women."
}
}
}
What I have tried is:
arr := make(map[string]interface{})
arrCate := make(map[string]interface{})
arrCateFlower := make(map[int]interface{})
arrCateColor := make(map[int]interface{})
arrCateFruit := make(map[int]interface{})
for index, data := range dataVals{
if(data.Category == "flower"){
arrCateFlower[index] = data
}
if(data.Category == "colors"){
arrCateColor[index] = data
}
if(data.Category == "fruits"){
arrCateFruit[index] = data
}
}
arrCate["flowers"] = arrCateFlower
arrCate["colors"] = arrCateColor
arrCate["fruits"] = arrCateFruit
arr["elements"] = arrCate
Where dataVals contain the unformatted data given at the top. By applying the above code I am able to get the proper output. But I don't think it is efficient way. If I try something like
arr := make(map[string]interface{})
arrCate := make(map[string]interface{})
for _, data := range dataVals{
arrCate[data.Category] = data
}
arr["elements"] = arrCate
Then I get something like:
"elements":{
"fruits":{
"id": 6,
"category": "fruits",
"name": "Mango",
"description": "Mango is one of my favorite fruits."
}
"flowers":{
"id": 5,
"category": "flowers",
"name": "Rose",
"description": "I love roses."
}
"colors":{
"id": 4,
"category": "colors",
"name": "Pink",
"description": "A romantic color, mostly liked by women."
}
}
the last elements of that particular category in the loop. I don't understand how can I get all the elements in the array without using any static values in code.
I have already spent hours in this. Can anyone please tell what am i missing in it?
https://play.golang.org/p/y-I6Fb_61R
I hope you can live with the additional outer {} pair.
And without the outer {} pair: https://play.golang.org/p/SSTgln0qJc
To not just have a bunch of links and to enable easy criticism of my solution by others, I include the code here, slightly redacted:
package main
import (
"fmt"
"encoding/json"
"log"
"strings"
)
var dataAsString = `` //put data between the ``
type Item struct {
Id int `json:"id"`
Category string `json:"category"`
Name string `json:"name"`
Description string `json:"description"`
}
type CategoryToItemSliceMap map[string][]Item
type CategoryToIndexItemMap map[string]map[int]Item
func main() {
// first read the data, we use a decoder as the input was given
// as a stream of seperate json objects and not a big single one.
decoder := json.NewDecoder(strings.NewReader(dataAsString))
var ourData []Item
for decoder.More() {
var it Item
err := decoder.Decode(&it)
if err != nil {
log.Fatalln(err)
}
ourData = append(ourData, it)
}
// collect items according to categories
catToItemSlice := CategoryToItemSliceMap{}
for _,v := range ourData {
catToItemSlice[v.Category] = append(catToItemSlice[v.Category],v)
}
// turn those slices into int -> Item maps so we get the index numbers
// in the encoded json
catToIndexItemMap := CategoryToIndexItemMap{}
for k,v := range catToItemSlice {
if catToIndexItemMap[k] == nil {
catToIndexItemMap[k] = map[int]Item{}
}
for index, item := range v {
catToIndexItemMap[k][index] = item
}
}
// easiest way to get the "elements: " without an additional outer {}
// brace pair
fmt.Printf("elements: ")
// We only have one json object in the output and that is a map, so we
// can use Unmarshal and don't need a streaming encoder. And get nice
// indentation with MarshalIndent.
out, err := json.MarshalIndent(catToIndexItemMap, "", " ")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(out))
}
// If atributes names equals , set value to fields
// target receive values of object
func Assign(target interface{}, object interface{}) {
t := reflect.ValueOf(target).Elem()
o := reflect.ValueOf(object).Elem()
for i := 0; i < o.NumField(); i++ {
for j := 0; j < t.NumField(); j++ {
if o.Type().Field(i).Name == t.Type().Field(j).Name {
t.Field(j).Set(o.Field(i))
}
}
}
}
// Use this exemple objects interfaces diffrents but fields are equals
// Assign(&target, &object)