【问题标题】:Adding Firebase data, dots and forward slashes添加 Firebase 数据、点和正斜杠
【发布时间】:2013-10-08 14:20:30
【问题描述】:

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

第一个问题是符号:点 '.'键中禁止,

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

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

您的键“/”中的正斜杠的第二个问题, 当您尝试像这样添加密钥时

        {'02/10/2013': true}

在firebase中你可以看到:

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

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

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

【问题讨论】:

标签: firebase firebase-realtime-database slash


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 编码似乎不能达到目的
    • @Philar,你是什么意思?
    • 对不起,我很迟钝,但编码 '[' 和 ']' 仍然会导致相同的 requests.exceptions.HTTPError: 400 Client Error: Bad Request 异常。最好避免在键中使用这些字符
    • @Philar,很有趣。我假设您正在使用 Python 请求库?您如何编码和发送请求?如果 Requests 正在撤消编码,您可能必须编码 两次
    • 提供的源链接错误。正确链接是:firebase.google.com/docs/database/usage/limits#data_tree
    【解决方案3】:

    我自己也遇到过同样的问题,为此我创建了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]);
    

    【讨论】:

    • 太棒了!谢谢?
    【解决方案4】:

    我是为 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;
    }
    

    【讨论】:

      【解决方案5】:

      我对这个问题感到恼火,所以我从@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】:

        如果您使用的是 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)
            }
        }
        

        【讨论】:

          【解决方案7】:

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

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

          结果将类似于 2017|07|24 02:39:37 而不是 2017/07/24 02:39:37

          【讨论】:

            【解决方案8】:

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

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

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

            【讨论】:

              【解决方案9】:

              高效的 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】:

                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'
                
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-05-29
                  • 2014-04-07
                  • 2016-06-14
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多