添加 Firebase 数据、点和正斜杠

Posted

技术标签:

【中文标题】添加 Firebase 数据、点和正斜杠【英文标题】:Adding Firebase data, dots and forward slashes 【发布时间】: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 的所有受限符号?

【问题讨论】:

使用github.com/cartant/firebase-key 【参考方案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://***.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'

【讨论】:

以上是关于添加 Firebase 数据、点和正斜杠的主要内容,如果未能解决你的问题,请参考以下文章

为啥字符和正斜杠被附加到我的 NSURL?

彻底搞懂反斜杠“”和正斜杠"/"的区别

Oracle 中的 JDBC 瘦连接字符串同时使用冒号和正斜杠

如何在VIM findsearch中转义反斜杠和正斜杠?[重复]

角度:自定义导入 - 指定文件路径

使用 nativescript-plugin-firebase 添加带有查询的事件侦听器