添加Firebase数据,点和正斜杠

时间:2013-10-02 08:27:57

标签: firebase firebase-realtime-database slash

我尝试使用firebase db, 我发现了非常重要的限制,这些限制在firebase帮助或FAQ中没有描述。

第一个问题是符号:点'。'禁止钥匙,

即。 firebase 拒绝(原因不明)下一步:

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

你的密钥'/'中的正斜杠的第二个问题, 当你尝试添加像这样的键

        {'02/10/2013': true}

在firebase中你可以看到:

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

你有任何想法如何解决(自动)? 可以设置一些标志,它是所有符号的字符串键吗? 当然,我可以在写入之前和读取之后每次解析/恢复数据,但是......

顺便说一句'。' '/' - firebase的所有限制符号?

11 个答案:

答案 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'