Given the two JTokens:
{ "Users": { "Name": "Carl" } }
and
{ "Users": [ { "Name": "Carl" }, {"Name": "Peter"} ] }
How can I tell if Users is a JProperty or JObject/JArray?
I need loop Users with
foreach (JObject User in myjobject["Users"]) { ... }
Solution
It was as simple as myjobject["Users"].GetType(). However, that didn't work in the Watch debugger window, but it worked at runtime. Hrmpff.
The Type property will tell you the type of the token you have.
switch(token.Type)
{
case JTokenType.Array:
break;
case JTokenType.String:
break;
}
Related
This question already has answers here:
How to deep copy a map and then clear the original?
(6 answers)
Closed 4 years ago.
What am I trying to do?
Copy a "default" struct into a new one when needed, keeping all it's values.
Details
I am trying to copy a Chat struct:
type ChatData struct {
User map[string]map[string]string `json:"user"`
Chat map[string]string `json:"chat"`
}
type Chat struct {
Settings map[string]map[string]interface{} `json:"settings"`
Data ChatData `json:"data"`
}
I only need to do this when a new chat is introduced, and I check for membership in a map[string]Chat.
//data is a map[string]Chat
if _, exists := data[event.Chat]; !exists {
data[event.Chat] = data["default"]
}
The full default struct is:
{
"default": {
"settings": {
"eightball": {
"messages": [
"yes",
"no",
"maybe"
]
},
"main": {
"blacklistedCommands": [],
"blacklistedUsers": [],
"error": "there was an error - ",
"maxConsecutive": 5,
"permissions": "You don't have permissions for that command.",
"success": "The command was successful.",
"whitelistedCommands": [],
"whitelistedUsers": []
}
},
"data": {
"user": {
"default": {
"admin": "false",
"consecutiveCommands": "0",
"nickname": "",
"sentMessages": "0"
},
"total": {
"admin": "false",
"consecutiveCommands": "0",
"nickname": "",
"sentMessages": "0"
}
},
"chat": {
"commandSender": "",
"lastMessage": "",
"lastSender": "",
"lastTimestamp": "",
"wasCommand":""
}
}
}
}
What have I tried
data[event.Chat] = data["default"]
// causes a reference
data[event.Chat] = &data["default"]
// cannot use &data["default"] (type *Chat) as type Chat in assignment
data[event.Chat] = *data["default"]
// invalid indirect of data["default"] (type Chat)
Do I need to change my use of map[string]Chat to map[string]*Chat and go with the second option? Pointers and references are not my specialty, so help would be appreciated.
Edit
whoever thought I was copying a map, what are you smoking?
I have found in previous cases, that an easy (though not the most efficient) way to copy a complex structure is to Marshal it, and then Unmarshal it into a new variable. This can be done with a variety of encodings. Two easy ones (included in the stdlib) would be json and gob.
There are also plenty of libraries using reflection to achieve a similar goal: https://github.com/jinzhu/copier
But like I said, though not as efficient, this is easy to reason about and takes just a single, simple function to achieve. If performance matters you should prefer gob over json. If performance REALLY matters, then you should prefer another solution altogether.
If you know the data structure it is best for you to fully define your structs. It will be much easier to manage than complex maps and interfaces with reflection to cast data types. Here is a tool to help you convert JSON to Go structs. It isn't perfect and you have to confirm the data types. You may even want to rip out the internal structs and create types for them for readability and maintenance, but it will save you some time.
https://mholt.github.io/json-to-go/
package main
import (
"encoding/json"
"fmt"
"log"
)
var rawDataExample = []byte(`{
"default": {
"settings": {
"eightball": {
"messages": [
"yes",
"no",
"maybe"
]
},
"main": {
"blacklistedCommands": [],
"blacklistedUsers": [],
"error": "there was an error - ",
"maxConsecutive": 5,
"permissions": "You don't have permissions for that command.",
"success": "The command was successful.",
"whitelistedCommands": [],
"whitelistedUsers": []
}
},
"data": {
"user": {
"default": {
"admin": "false",
"consecutiveCommands": "0",
"nickname": "",
"sentMessages": "0"
},
"total": {
"admin": "false",
"consecutiveCommands": "0",
"nickname": "",
"sentMessages": "0"
}
},
"chat": {
"commandSender": "",
"lastMessage": "",
"lastSender": "",
"lastTimestamp": "",
"wasCommand":""
}
}
}
}
`)
type Settings struct {
Default struct {
Settings struct {
Eightball struct {
Messages []string `json:"messages"`
} `json:"eightball"`
Main struct {
BlacklistedCommands []string `json:"blacklistedCommands"`
BlacklistedUsers []string `json:"blacklistedUsers"`
Error string `json:"error"`
MaxConsecutive int `json:"maxConsecutive"`
Permissions string `json:"permissions"`
Success string `json:"success"`
WhitelistedCommands []string `json:"whitelistedCommands"`
WhitelistedUsers []string `json:"whitelistedUsers"`
} `json:"main"`
} `json:"settings"`
Data struct {
User struct {
Default struct {
Admin string `json:"admin"`
ConsecutiveCommands string `json:"consecutiveCommands"`
Nickname string `json:"nickname"`
SentMessages string `json:"sentMessages"`
} `json:"default"`
Total struct {
Admin string `json:"admin"`
ConsecutiveCommands string `json:"consecutiveCommands"`
Nickname string `json:"nickname"`
SentMessages string `json:"sentMessages"`
} `json:"total"`
} `json:"user"`
Chat struct {
CommandSender string `json:"commandSender"`
LastMessage string `json:"lastMessage"`
LastSender string `json:"lastSender"`
LastTimestamp string `json:"lastTimestamp"`
WasCommand string `json:"wasCommand"`
} `json:"chat"`
} `json:"data"`
} `json:"default"`
}
type Data struct {
Events map[string]Settings
}
func main() {
var settings Settings
err := json.Unmarshal(rawDataExample, &settings)
if err != nil {
log.Fatal(err)
}
event := "foo"
d := Data{
Events: make(map[string]Settings),
}
if _, ok := d.Events[event]; !ok {
// event doesn't exist
// add it
d.Events[event] = settings
fmt.Println("event added")
}
if _, ok := d.Events[event]; !ok {
// event exist, this will never be happen
// sanity check
fmt.Println("this will never be printed")
}
fmt.Printf("%+v\n", d)
}
Super simple fix, just wanted to get some input before I did it. Changed the map[string]Chat > map[string]*Chat and made a new *Chat, then copied the data.
data[event.Chat] = &Chat{}
*data[event.Chat] = *data["default"]
I can't seem to find how to correctly call PutItem for a StringSet in DynamoDB through API Gateway. If I call it like I would for a List of Maps, then I get objects returned. Example data is below.
{
"eventId": "Lorem",
"eventName": "Lorem",
"companies": [
{
"companyId": "Lorem",
"companyName": "Lorem"
}
],
"eventTags": [
"Lorem",
"Lorem"
]
}
And my example template call for companies:
"companies" : {
"L": [
#foreach($elem in $inputRoot.companies) {
"M": {
"companyId": {
"S": "$elem.companyId"
},
"companyName": {
"S": "$elem.companyName"
}
}
} #if($foreach.hasNext),#end
#end
]
}
I've tried to call it with String Set listed, but it errors out still and tells me that "Start of structure or map found where not expected" or that serialization failed.
"eventTags" : {
"SS": [
#foreach($elem in $inputRoot.eventTags) {
"S":"$elem"
} #if($foreach.hasNext),#end
#end
]
}
What is the proper way to call PutItem for converting an array of strings to a String Set?
If you are using JavaScript AWS SDK, you can use document client API (docClient.createSet) to store the SET data type.
docClient.createSet - converts the array into SET data type
var docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName:table,
Item:{
"yearkey": year,
"title": title
"product" : docClient.createSet(['milk','veg'])
}
};
I'm working on setting up validaton rules for a Firebase data structure, created using the Bolt compiler.
I'm currently having the Bolt statement below:
path /sharedEvents/{share} is Boolean[] {
read() { isMailOfCurrentUser( share ) }
create() { isOwnerOfEvent( ...) } //NOT YET CORRECT!
delete() { isOwnerOfEvent( prior(...) } //NOT YET CORRECT!
}
With this, I'm trying to achieve that:
Only users having a mail corresponding to the key of 'share' are allowed to read the data (they use this date to retrieve the key of events shared with them.
Only the owner of an event is able to add/remove the key for his event to the list of shared events.
This second point is where I'm running into trouble -I'm not able to create the create/delete rules- since I have no idea how to reference the keys of the boolean values in the validation rule...
Example data in Firebase for the above bolt statement:
sharedEvents
ZW5kc3dhc0BldmVyeW1hMWwuYml6
-BDKBEvy-hssDhKqVF5w: true
-FDKBEvy-hsDsgsdsf5w: true
-ADBEvy-hfsdsdKqVF5w: true
aXQnc251bWJlcnNAbWExbDJ1LnVz
-KBEvy-hsDhH6OKqVF5w: true
To clarify the needs on this example:
Only user with mail 'ZW5kc3dhc0BldmVyeW1hMWwuYml6' is able to read the three nested childs.
Only the owner of event '-BDKBEvy-hssDhKqVF5w' should be able to create/delete this value. (the same for the other event key/boolean pairs).
My question: is this setup going to work (and how to setup the create/delete rules)? Or is this not going to work and should I rethink/structure the data?
Any help is appreciated!
-----------------OUTPUT JSON FILE------------------------------------------
The question above has been answered, this section is showing the resulting json
"sharedEvents": {
"$share": {
".read": "<removed for readability>",
"$event": {
".validate": "newData.isBoolean()",
".write": "<removed for readability>"
}
}
},
Thanks again for your quick support!
You'll need a nested path statement to handle the restriction on the events (the nodes under /sharedEvents/$mail/$eventid). I quickly prototyped with this JSON structure:
{
"events": {
"-ADBEvy-hfsdsdKqVF5w": {
"name": "Event 1",
"ownerMail": "aXQnc251bWJlcnNAbWExbDJ1LnVz"
},
"-BDKBEvy-hssDhKqVF5w": {
"name": "Event 2",
"ownerMail": "aXQnc251bWJlcnNAbWExbDJ1LnVz"
},
"-FDKBEvy-hsDsgsdsf5w": {
"name": "Event 3",
"ownerMail": "aXQnc251bWJlcnNAbWExbDJ1LnVz"
},
"-KBEvy-hsDhH6OKqVF5w": {
"name": "Event 3",
"ownerMail": "ZW5kc3dhc0BldmVyeW1hMWwuYml6"
}
},
"sharedEvents": {
"ZW5kc3dhc0BldmVyeW1hMWwuYml6": {
"-ADBEvy-hfsdsdKqVF5w": true,
"-BDKBEvy-hssDhKqVF5w": true,
"-FDKBEvy-hsDsgsdsf5w": true
},
"aXQnc251bWJlcnNAbWExbDJ1LnVz": {
"-KBEvy-hsDhH6OKqVF5w": true
}
},
"userMails": {
"peter": "aXQnc251bWJlcnNAbWExbDJ1LnVz",
"puf": "ZW5kc3dhc0BldmVyeW1hMWwuYml6"
}
}
And came up with these rules:
path /sharedEvents/{share} {
read() { isMailOfCurrentUser(share) }
}
path /sharedEvents/{share}/{event} is Boolean {
create() { isOwnerOfEvent(event) }
delete() { isOwnerOfEvent(prior(event)) }
}
isMailOfCurrentUser(share) { true }
getMailOfCurrentUser(uid) { root.ownerMails.uid }
getEventOwnerMail(event) { root.events.event.ownerMail }
isOwnerOfEvent(event) { getMailOfCurrentUser(auth.uid) == getEventOwnerMail(event) }
Ignoring any mistakes on my end, this should be the basics of the authorization structure you're looking for.
I'm building an app where I need to process 5k+ tasks in small batches. For that I have a queue of tasks that is stored in a Firebase. I'd like to be able to pull certain amount of tasks with empty status, update their status and write back.
Currently I don't see how I can pull data where a certain field is empty. Is it possible? If not, what would be the alternative solution?
UPDATED 02/12. Here is the data structure that I have:
{
"-KAMnc89C5Yi_ef18ewc" : {
"0": {
"url": "https://excample.com/url",
"status": "done"
},
"1": {
"url": "https://excample.com/url1"
},
"2": {
"url": "https://excample.com/ur2"
},
"3": {
"url": "https://excample.com/ur3"
}
}
And this is the query I'm using:
queueRef.orderByChild('status').equalTo(null).limitToFirst(1).once('value', function(snapshot) {
console.log(snapshot.val());
});
queueRef points to "-KAMnc89C5Yi_ef18ewc" from the data above.
I expect to get one object - "1", but instead I'm getting all of them. Is there something I'm missing?
Firebase doesn't allow you to store a property without a value. That simply means that the property doesn't exist.
Luckily this doesn't really matter too much, because this seems to work. Given this data structure:
{
"-KADbswYg3FiQF78mmUf": {
"name": "task1",
"status": "done"
},
"-KADbugr7QzTx0s93Fs0": {
"name": "task2"
},
"-KADbvKvBgiAXxnQvoBp": {
"name": "task3"
}
}
This works:
ref.orderByChild('status').equalTo(null).once('value', function(snapshot) {
console.log(snapshot.val());
})
This prints task2 and task3.
Use the DataSnapshot.exists()
This will returns true if this snapshot contains any data. If not it will return false. According to the documentation here. It is slightly more efficient than using snapshot.val() !== null.
With a data structure like this:
{
"girlfriend": {
"first": "Pamala",
"last": "Anderson"
}
}
And a firebase call like this:
var ref = new Firebase("https://myURL/girlfriend/Pamala");
ref.once("value", function(snapshot) {
var a = snapshot.exists();
// a === true
var b = snapshot.child("girlfriend").exists();
// b === true
var c = snapshot.child("girlfriend/first").exists();
// c === true
var d = snapshot.child("girlfriend/middle").exists();
// d === false (because there is no "name/middle" girlfriend in the data snapshot)
});
I store my data on Firebase with the format
{
"list": [
"id created by Firebase": {
"foo": "bar"
},
"another id created by Firebase": {
"foo": "bar"
},
...
]
}
I would like to create an index on foo, using Firebase Rules.
However, according to Firebase doc, I need to know the specific ID created by Firebase to put in the rules specification.
Does anyone know of a way to get around this?
You don't need to know the specific ID, that wouldn't be possible. You simply need to have an .indexOn rule on the parent node. https://www.firebase.com/docs/security/guide/indexing-data.html
{
"rules": {
"list": {
".indexOn": ["foo"]
}
}
}
If you need to add additional rules for the children, then you add those normally like so:
{
"rules": {
"list": {
".indexOn": ["foo"],
"$item": {
"foo": {
".validate": "newData.isString()"
}
}
}
}
}