我尝试使用firebase db, 我发现了非常重要的限制,这些限制在firebase帮助或FAQ中没有描述。
第一个问题是符号:点'。'禁止钥匙,
即。 firebase 拒绝(原因不明)下一步:
nameRef.child('Henry.Morgan@caribbean.sea').set('Pirat');
你的密钥'/'中的正斜杠的第二个问题, 当你尝试添加像这样的键
{'02/10/2013': true}
在firebase中你可以看到:
'02': {
'10': {
'2013': true
}
}
你有任何想法如何解决(自动)? 可以设置一些标志,它是所有符号的字符串键吗? 当然,我可以在写入之前和读取之后每次解析/恢复数据,但是......
顺便说一句'。' '/' - firebase的所有限制符号?
答案 0 :(得分:22)
添加子02/10/2013
在Firebase中创建结构的原因是正斜杠导致创建新级别。
因此,我认为您使用类似于firebaseRef.child('02/10/2013').set(true)
的内容的行等同于firebaseRef.child('02').child('10').child('2013').set(true)
。
为避免在参考密钥名称(source)中无法使用以下字符的问题,
- 。 (期间)
- $(美元符号)
- [(左方括号)
- ](右方括号)
- #(哈希或井号)
- /(正斜线)
我们可以使用JavaScript的内置编码功能之一,因为据我所知,Firebase 不提供内置方法。这是一个贯穿始终,看看哪个对我们的目的最有效:
var forbiddenChars = '.$[]#/'; //contains the forbidden characters
escape(forbiddenChars); //results in ".%24%5B%5D%23/"
encodeURI(forbiddenChars); //results in ".%24%5B%5D%23%2F"
encodeURIComponent(forbiddenChars); //results in ".%24%5B%5D%23%2F"
显然,最有效的解决方案是encodeURIComponent
。但是,它并没有解决所有我们的问题。 .
字符仍然存在问题,如上述测试所示,并尝试encodeURIComponent
您的测试电子邮件地址。我的建议是在encodeURIComponent
之后链接一个替换函数来处理句点。
以下是两个示例案例的解决方案:
encodeURIComponent('Henry.Morgan@caribbean.sea').replace(/\./g, '%2E') //results in "Henry%2EMorgan%40caribbean%2Esea"
encodeURIComponent('02/10/2013'); //results in "02%2F10%2F2013"
由于最终结果都可以安全地插入Firebase作为键名,因此唯一的另一个问题是从Firebase读取后解码,可以使用replace('%2E', '.')
和简单decodeURIComponent(...)
来解决。
答案 1 :(得分:8)
我自己也遇到了同样的问题,为此我创建了firebase-encode。
与选择的答案不同,firebase-encode仅编码不安全的字符(./ [] #$)和%(由于编码/解码的工作原理而必需)。
它会保留其他可以安全用作firebase密钥的特殊字符,而encodeURIComponent
将对它们进行编码。
以下是详细信息的源代码:
// http://stackoverflow.com/a/6969486/692528
const escapeRegExp = (str) => str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
const chars = '.$[]#/%'.split('');
const charCodes = chars.map((c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
const charToCode = {};
const codeToChar = {};
chars.forEach((c, i) => {
charToCode[c] = charCodes[i];
codeToChar[charCodes[i]] = c;
});
const charsRegex = new RegExp(`[${escapeRegExp(chars.join(''))}]`, 'g');
const charCodesRegex = new RegExp(charCodes.join('|'), 'g');
const encode = (str) => str.replace(charsRegex, (match) => charToCode[match]);
const decode = (str) => str.replace(charCodesRegex, (match) => codeToChar[match]);
答案 2 :(得分:6)
https://www.firebase.com/docs/creating-references.html记录了字符限制 - 您不能在密钥名称中使用'。','/','[',']','#'和'$'。没有自动的方式来逃避这些角色,我建议完全避免使用它们或创建自己的转义/转义机制。
答案 3 :(得分:6)
我为Java编写了这个(因为我来到这里期待一个java实现):
public static String encodeForFirebaseKey(String s) {
return s
.replace("_", "__")
.replace(".", "_P")
.replace("$", "_D")
.replace("#", "_H")
.replace("[", "_O")
.replace("]", "_C")
.replace("/", "_S")
;
}
public static String decodeFromFirebaseKey(String s) {
int i = 0;
int ni;
String res = "";
while ((ni = s.indexOf("_", i)) != -1) {
res += s.substring(i, ni);
if (ni + 1 < s.length()) {
char nc = s.charAt(ni + 1);
if (nc == '_') {
res += '_';
} else if (nc == 'P') {
res += '.';
} else if (nc == 'D') {
res += '$';
} else if (nc == 'H') {
res += '#';
} else if (nc == 'O') {
res += '[';
} else if (nc == 'C') {
res += ']';
} else if (nc == 'S') {
res += '/';
} else {
// this case is due to bad encoding
}
i = ni + 2;
} else {
// this case is due to bad encoding
break;
}
}
res += s.substring(i);
return res;
}
答案 4 :(得分:2)
如果您使用的是Swift 3,这对我有用(在操场上试试):
var str = "this.is/a#crazy[string]right$here.$[]#/"
if let strEncoded = str.addingPercentEncoding(withAllowedCharacters: .alphanumerics) {
print(strEncoded)
if let strDecoded = strEncoded.removingPercentEncoding {
print(strDecoded)
}
}
答案 5 :(得分:1)
我对这个问题感到恼火所以我从@ sushain97那里得到答案(谢谢!)并构建了一个深度编码器/解码器。
https://www.npmjs.com/package/firebase-key-encode
基本用法:
var firebaseKeyEncode = require('firebase-key-encode');
firebaseKeyEncode.encode('my.bad.key');
// Output: my%2Ebad%2Ekey
深度使用:
var firebaseKeyEncode = require('firebase-key-encode');
var badTree = {
"pets": [
{
"jimmy.choo": 15}
],
"other.key": 5
}
firebaseKeyEncode.deepEncode(badTree);
// Output: {
// "pets": [
// {
// "jimmy%2Echoo": 15}
// ],
// "other%2Ekey": 5
// }
答案 6 :(得分:1)
就个人而言,我发现了一个简单易行的黑客,遇到了我遇到的同样问题
我使用dateTime string
并使用replace('/','|')
结果将类似于2017|07|24 02:39:37
而不是2017/07/24 02:39:37
。
答案 7 :(得分:1)
我没有看到内置FireBase编码器的“自动”键。
这是一个Java解决方案。
我构建了这个,josue.0的答案的简化版本,但我认为这是更好的代码,因为他的版本可能会导致问题。很多人会在代码中使用_P或_D,因此需要更复杂,更不可能。
public static String encodeForFirebaseKey (String s) {
s = s.replace(".", "_P%ë5nN*")
.replace("$", "_D%5nNë*")
.replace("#", "_H%ë5Nn*")
.replace("[", "_Oë5n%N*")
.replace("]", "_5nN*C%ë")
.replace("/", "*_S%ë5nN")
;
return s;
}
public static String decodeFromFirebaseKey(String s) {
s = s.replace("_P%ë5nN*", ".")
.replace("_D%5nNë*", "$")
.replace("_H%ë5Nn*", "#")
.replace("_Oë5n%N*", "[")
.replace("_5nN*C%ë", "]")
.replace("*_S%ë5nN", "/");
return s;
答案 8 :(得分:0)
即使不是OP要求的,
但以我的经验,最好不要使用.push()
创建一个ID,而不是使用这种可疑的密钥,
以及电子邮件,日期等其他内容另存为专用字段的内容。
$id: {
email: "Henry.Morgan@caribbean.sea"
}
P.S。不要试图通过在密钥中插入应包含的内容来节省音量。
过早的优化是万恶之源(c)。
答案 9 :(得分:0)
高效的 C# 实现(适用于 Unity 和 .net)。基于@josue.0 的回答。
public static string EncodeFirebaseKey(string s) {
StringBuilder sb = new StringBuilder();
foreach (char c in s) {
switch (c) {
case '_':
sb.Append("__");
break;
case '$':
sb.Append("_D");
break;
case '.':
sb.Append("_P");
break;
case '#':
sb.Append("_H");
break;
case '[':
sb.Append("_O");
break;
case ']':
sb.Append("_C");
break;
case '/':
sb.Append("_S");
break;
default:
sb.Append(c);
break;
}
}
return sb.ToString();
}
public static string DecodeFirebaseKey(string s) {
StringBuilder sb = new StringBuilder();
bool underscore = false;
for (int i = 0; i < s.Length; i++) {
if (underscore) {
switch (s[i]) {
case '_':
sb.Append('_');
break;
case 'D':
sb.Append('$');
break;
case 'P':
sb.Append('.');
break;
case 'H':
sb.Append('#');
break;
case 'O':
sb.Append('[');
break;
case 'C':
sb.Append(']');
break;
case 'S':
sb.Append('/');
break;
default:
Debug.LogWarning("Bad firebase key for decoding");
break;
}
underscore = false;
} else {
switch (s[i]) {
case '_':
underscore = true;
break;
default:
sb.Append(s[i]);
break;
}
}
}
return sb.ToString();
}
答案 10 :(得分:0)
Python 实现
_escape = {'&': '&&',
'$': '&36',
'#': '&35',
'[': '&91',
']': '&93',
'/': '&47',
'.': '&46'}
_unescape = {e: u for u, e in _escape.items()}
def escape_firebase_key(text):
return text.translate(str.maketrans(_escape))
def unescape_firebase_key(text):
chunks = []
i = 0
while True:
a = text[i:].find('&')
if a == -1:
return ''.join(chunks + [text[i:]])
else:
if text[i+a:i+a+2] == '&&':
chunks.append('&')
i += a+2
else:
s = text[i+a:i+a+3]
if s in _unescape:
chunks.append(text[i:i+a])
chunks.append(_unescape[s])
i += a+3
else:
raise RuntimeError('Cannot unescape')
还有一些测试用例:
test_pairs = [('&hello.', '&&hello&46'),
('&&&', '&&&&&&'),
('some@email.com', 'some@email&46com'),
('#$[]/.', '&35&36&91&93&47&46')]
for u, e in test_pairs:
assert escape_firebase_key(u) == e, f"escaped '{u}' is '{e}', but was '{escape_firebase_key(u)}'"""
assert unescape_firebase_key(e) == u, f"unescaped '{e}' is '{u}', but was '{unescape_firebase_key(e)}'"
try:
unescape_firebase_key('&error')
assert False, 'Must have raised an exception here'
except RuntimeError as ex:
assert str(ex) == 'Cannot unescape'