22

I try to use firebase db, I found very important restrictions, which are not described in firebase help or FAQ.

First problem is that symbol: dot '.' prohibited in keys,

i.e. firebase reject (with unknown reason) next:

        nameRef.child('Henry.Morgan@caribbean.sea').set('Pirat');

Second problem with forward slashes in your keys '/', when you try to add key like this

        {'02/10/2013': true}

In firebase you can see:

         '02': {
             '10': {
                 '2013': true
             }
         }       

Have you got any ideas how to solve it (automatically)? May be set some flag that it is string key with all symbols? Of course, I can parse/restore data every time before write and after read, but...

By the way '.' '/' - all restricted symbols for firebase ?

4

10 回答 10

33

添加子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(...).

于 2013-10-02T22:52:26.123 回答
11

我自己也遇到了同样的问题,为此我创建了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]);
于 2016-07-04T02:10:26.993 回答
8

字符限制记录在https://www.firebase.com/docs/creating-references.html - 您不能使用 '.'、'/'、'['、']'、'#' 和 '$'在键名中。没有自动转义这些字符的方法,我建议完全避免使用它们或创建自己的转义/取消转义机制。

于 2013-10-02T17:16:07.127 回答
8

我为 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;
}
于 2016-09-18T18:45:48.943 回答
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)
    }
}
于 2017-05-31T18:39:28.900 回答
1

就我个人而言,我发现了一个简单易行的技巧来解决我遇到的同样问题

我拿了dateTime string它并使用它转换它replace('/','|')

结果将是这样的,2017|07|24 02:39:37而不是2017/07/24 02:39:37.

于 2017-07-23T18:48:52.507 回答
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
// }
于 2016-09-29T15:16:52.863 回答
0

尽管这不是 OP 所要求的,
但根据我的经验,与其使用这种可疑的键,不如让.push()创建一个 id
和其他东西 - 电子邮件、日期等保存为专用字段的内容。

$id: {
   email: "Henry.Morgan@caribbean.sea"
}

PS 不要试图通过将内容插入密钥来节省音量。
过早的优化是万恶之源(c)。

于 2020-08-18T06:27:13.963 回答
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();
        }
于 2021-05-03T20:51:18.463 回答
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'

于 2021-07-02T17:17:23.837 回答