2015-05-19 27 views
0

我有一個運行在服務器上的C++程序,返回JSON格式的數據,並使用boost進行序列化。在某些情況下,JSON數據包的一個值部分是另一個序列化爲字符串的JSON數據包。在我的開發機器上的XCode中運行,程序將帶有轉義引號的值數據返回給我,這些數據在Linux系統上編譯時沒有。有什麼辦法強制推動在Linux上做到這一點或一個很好的解決方法? (我們已經修復了一般缺失值引號的錯誤)。Boosts JSON解析器在MacOS上轉義引號但在Linux上不轉換

下方是作爲例子的第一行:

local: 
"stations": "{\n \"area CHALLENGE_FUTURE\": \"{\\n \\\"areaname CHALLENGE_FUTURE\\\": [...]" 
online: 
"stations": "{\n "area CHALLENGE_FUTURE": "{\\n "areaname CHALLENGE_FUTURE": [...]" 

升壓版本爲1.57,編譯器(本地Xcode和在線的Linux)的鐺-Linux的3.5。 (見編輯)

這裏的簡化代碼:

// ----- STL 
#include <iostream> 
#include <string> 
#include <map> 

// ------ boost 
#include <boost/asio.hpp> 
#include <boost/foreach.hpp> 


//formatter_base.hpp 
//------------------------------------------------------------------------------ 
class formatter_base 
{ 
protected: 
    std::map<std::string, std::string> datas; 

public: 
    virtual ~formatter_base() {} 
    void setvalue(std::string key, std::string value) 
    { 
     datas[key] = value; 
    } 

    std::string getvalue(std::string key) 
    { 
     return datas[key]; 
    } 

    bool containsKey(std::string key) 
    { 
     return (datas.find(key) != datas.end()); 
    } 

    virtual void deserialize(char *data, const std::size_t size) = 0; 
    virtual std::string serialize(std::vector<std::string> keys) = 0; 
}; 

//json_formatter.hpp 
class json_formatter : public formatter_base 
{ 
public: 
    virtual void deserialize(char *data, const std::size_t size); 
    virtual std::string serialize(std::vector<std::string> keys); 
}; 


//datapacket.hpp 
//------------------------------------------------------------------------------ 
class server; 
extern server *tcp_server; 

class datapacket 
{ 
    static const char id[4]; 

public: 
    enum DataFormat { BINARY = 0, JSON, XML }; 

    std::string ip; 
    bool useHeader; 

    datapacket() : useHeader(false), packet_data(NULL) {} 
    datapacket(DataFormat format); 
    std::vector<char> process(char *data, std::size_t size, std::string ip); 

    std::string getvalue(std::string key) 
    { 
     return packet_data->getvalue(key); 
    } 

    void setvalue(std::string key, std::string value) 
    { 
     packet_data->setvalue(key, value); 
    } 

    bool containsKey(std::string key) 
    { 
     return packet_data->containsKey(key); 
    } 

    std::vector<char> serialize(); 
    std::string  toString(); 

private: 
    bool deserialize(char *data, std::size_t size); 
    std::string serialize_data(std::vector<std::string> keys); 

    formatter_base *packet_data; 
}; 

//datapacket.cpp 
//------------------------------------------------------------------------------ 
#include <boost/iostreams/stream.hpp> 
#include <boost/foreach.hpp> 
#include <boost/property_tree/ptree.hpp> 
#include <boost/property_tree/json_parser.hpp> 
#include <string.h> 

datapacket::datapacket(DataFormat format) 
: useHeader(false) 
{ 
    if(format == JSON) 
    { 
     packet_data = new json_formatter(); 
    } 
    else 
    { 
     packet_data = NULL; 
    } 
} 


std::vector<char> datapacket::process(
             char *data, std::size_t size, std::string ip) 
{ 
    //std::cout << "datapacket::process" << std::endl; 

    this->ip = ip; 
    std::vector<char> ret; 

    if (!deserialize(data, size)) 
     return ret; 

    std::vector<std::string> keys; 
    std::string result; 

    /*extern void process(datapacket& gamedata); 
    process(*this);*/ 

    ret.push_back('a'); 
    ret.push_back('c'); 
    ret.push_back('k'); 

    return ret; 
} 

bool datapacket::deserialize(char *data, std::size_t size) 
{ 
    packet_data = new json_formatter(); 
    packet_data->deserialize(data, size); 
    return true; 
} 

std::string datapacket::serialize_data(std::vector<std::string> keys) 
{ 
    return packet_data->serialize(keys); 
} 

std::vector<char> datapacket::serialize() 
{ 
    std::vector<std::string> keys; 
    std::string str = serialize_data(keys); 
    std::vector<char> ret; 
    ret.assign(str.begin(), str.end()); 

    return ret; 
} 

std::string datapacket::toString() 
{ 
    std::vector<std::string> keys; 
    std::string str = serialize_data(keys); 

    return str; 
} 


//json_formatter.cpp 
//------------------------------------------------------------------------------ 
using namespace boost::property_tree; 

void json_formatter::deserialize(char *data, const std::size_t size) 
{ 
    std::stringstream ss; 
    ss.write(data, size); 

    // std::cout << "ss: " << ss.str() << std::endl; 

    ptree pt; 
    read_json(ss, pt); 

    BOOST_FOREACH(ptree::value_type &v, pt) 
    { 
     //log all received data 
     //std::cout << v.first.data() << ": " << v.second.data() << std::endl; 

     datas[v.first.data()] = v.second.data(); 
    } 
} 

///-workaround 1.57 json 
template <typename T> 
struct my_id_translator 
{ 
    typedef T internal_type; 
    typedef T external_type; 

    boost::optional<T> get_value(const T &v) { return v.substr(1, v.size() - 2) ; } 
    boost::optional<T> put_value(const T &v) { return '"' + v + '"'; } 
}; 
/// 
std::string json_formatter::serialize(std::vector<std::string> keys) 
{ 


    ptree pt; 

    if(keys.empty()) 
    { 
     typedef std::map<std::string, std::string> mapType; 
     BOOST_FOREACH(const mapType::value_type& myPair, datas) 
     { 
      //workaround for wrong formatted string 
      if((BOOST_VERSION == 105700) && (BOOST_OS_LINUX)) 
      { 
       //1.57 
       pt.put(myPair.first, myPair.second, my_id_translator<std::string>()); 
      } 
      else 
      { 
       //1.54 
       pt.put(myPair.first, myPair.second); 
      } 
      //std::cout << myPair.first << ": " << myPair.second << std::endl; 
     } 
    } 
    else 
    { 
     BOOST_FOREACH(std::string key, keys) 
     { 
      //workaround for wrong formatted string 
      if(BOOST_VERSION == 105700) 
      { 
#if BOOST_OS_LINUX 
       pt.put(key, "\"" + datas[key] + "\"", my_id_translator<std::string>()); 
#else 
       pt.put(key, datas[key], my_id_translator<std::string>()); 
#endif 
      } 
      else 
      { 
       pt.put(key, datas[key]); 
      } 
      //  std::cout << key << ": " << datas[key] << std::endl; 
     } 
    } 

    std::stringstream ss; 
    write_json(ss, pt); 
    std::string str = ss.str(); 

    // WORKAROUND 
    // Replace all escaped backslashes 
    // This was because some clients couldn't interpret "command\\/read" 
    std::string oldStr = "\\/"; 
    std::string newStr = "/"; 

    std::size_t pos = 0; 
    while((pos = str.find(oldStr)) != std::string::npos){ 
     str = str.replace(pos, oldStr.length(), newStr); 
     pos += newStr.length(); 
    } 
    // /WORKAROUND 

    //std::cout << "Serialize message:" << std::endl; 
    //std::cout << str << std::endl; 
    return str; 
} 


//main.cpp 
//------------------------------------------------------------------------------ 
class dataClass 
{ 
public: 
    dataClass() {} 
    std::string name; 
}; 

class innerDataClass 
{ 
public: 
    innerDataClass() {} 
    std::string name; 
    int   score; 
    std::string baseClassName; 
}; 

using boost::asio::ip::tcp; 


namespace stdpatch 
{ 
    template <typename T> std::string to_string(const T& n) 
    { 
     std::ostringstream stm; 
     stm << n; 
     return stm.str(); 
    } 
} 

std::map<std::string, dataClass>  listDC; 
std::map<std::string, innerDataClass> listIDC; 

void Init() 
{ 
    //Some initial values 
    dataClass d1; d1.name = "dataClass1"; listDC["d1"] = d1; 
    dataClass d2; d2.name = "dataClass2"; listDC["d2"] = d2; 

    innerDataClass i1; i1.name = "innerClass1"; i1.baseClassName = "dataClass1"; 
    i1.score = 5; listIDC["i1"] = i1; 
    innerDataClass i2; i2.name = "innerClass2"; i2.baseClassName = "dataClass1"; 
    i1.score = 21; listIDC["i2"] = i2; 
    innerDataClass i3; i3.name = "innerClass3"; i1.baseClassName = "dataClass2"; 
    i1.score = 1; listIDC["i3"] = i3; 
} 

//returns JSON 
datapacket GetJSON() 
{ 
    std::pair<std::string, dataClass>  baseClassPair; 
    std::pair<std::string, innerDataClass> innerClassPair; 

    datapacket baseClasses (datapacket::JSON); 
    baseClasses.setvalue("comment", "this holds all the base classes"); 
    BOOST_FOREACH(baseClassPair, listDC) 
    { 
     datapacket baseClassData (datapacket::JSON); 
     baseClassData.setvalue("dataName", baseClassPair.first); 
     BOOST_FOREACH(innerClassPair, listIDC) 
     { 
      if (innerClassPair.second.baseClassName == baseClassPair.second.name) 
      { 
       datapacket innerClassData (datapacket::JSON); 
       innerClassData.setvalue(
             "name", innerClassPair.second.name); 
       innerClassData.setvalue(
             "score", stdpatch::to_string(innerClassPair.second.score)); 

       baseClassData.setvalue(
             "inner " + innerClassPair.first, innerClassData.toString()); 
      } 
     } 
     baseClasses.setvalue("base " + baseClassPair.first, baseClassData.toString()); 
    } 

    datapacket packet (datapacket::JSON); 
    packet.setvalue("comment", "this is the base-packet"); 
    packet.setvalue("baseClasses", baseClasses.toString()); 
    return packet; 
} 


int main(int argc, char* argv[]) 
{ 
    Init(); 
    datapacket packet (datapacket::JSON); 
    packet = GetJSON(); 

    std::cout << std::endl << std::endl 
    << "------------- RESULT --------------" 
    << std::endl << std::endl; 

    std::cout << packet.toString() << std::endl; 

    return 0; 
} 

預期輸出應該是:

------------- RESULT -------------- 

baseClasses: { 
    "base d1": "{\n \"dataName\": \"d1\",\n \"inner i1\": \"{\\n \\\"gameID\\\": \\\"5\\\",\\n \\\"name\\\": \\\"innerClass1\\\"\\n}\\n\",\n \"inner i2\": \"{\\n \\\"gameID\\\": \\\"1989860191\\\",\\n \\\"name\\\": \\\"innerClass2\\\"\\n}\\n\"\n}\n", 
    "base d2": "{\n \"dataName\": \"d2\"\n}\n", 
    "comment": "this holds all the base classes" 
} 

comment: this is the base-packet 
{ 
    "baseClasses": "{\n \"base d1\": \"{\\n \\\"dataName\\\": \\\"d1\\\",\\n \\\"inner i1\\\": \\\"{\\\\n \\\\\\\"gameID\\\\\\\": \\\\\\\"5\\\\\\\",\\\\n \\\\\\\"name\\\\\\\": \\\\\\\"innerClass1\\\\\\\"\\\\n}\\\\n\\\",\\n \\\"inner i2\\\": \\\"{\\\\n \\\\\\\"gameID\\\\\\\": \\\\\\\"1989860191\\\\\\\",\\\\n \\\\\\\"name\\\\\\\": \\\\\\\"innerClass2\\\\\\\"\\\\n}\\\\n\\\"\\n}\\n\",\n \"base d2\": \"{\\n \\\"dataName\\\": \\\"d2\\\"\\n}\\n\",\n \"comment\": \"this holds all the base classes\"\n}\n", 
    "comment": "this is the base-packet" 
} 

但引號是在我的服務器情況下轉義:

comment: this is the base-packet 
{ 
    "baseClasses": "{\n "base d1": "{\\n "dataName": "d1",\\n "inner i1": "{\\\\n "gameID": "5",\\\\n "name": "innerClass1"\\\\n}\\\\n",\\n "inner i2": "{\\\\n "gameID": "1989860191",\\\\n "name": "innerClass2"\\\\n}\\\\n"\\n}\\n",\n "base d2": "{\\n \\\"dataName": "d2"\\n}\\n",\n "comment": "this holds all the base classes"\n}\n", 
    "comment": "this is the base-packet" 
} 

爲了測試問題是在write_json還是transit中我做了一個簡單的JS軟件包:

datapacket testData(datapacket::JSON); 
testData.setvalue("text", "\"world\""); 
testData.setvalue("inner1", testData.toString()); 
testData.setvalue("inner2", testData.toString()); 

結果如下:

XCode: 
{ 
    "inner1": "{\n \"text\": \"\\\"world\\\"\"\n}\n", 
    "inner2": "{\n \"inner1\": \"{\\n \\\"text\\\": \\\"\\\\\\\"world\\\\\\\"\\\"\\n}\\n\",\n \"text\": \"\\\"world\\\"\"\n}\n", 
    "text": "\"world\"" 
} 

Server: 
{ 
    "inner1": "{\n "text": ""world""\n}\n" 
    "inner2": "{\n "text": ""world"",\n "inner1": "{\\n "text": ""world""\\n}\\n"\n}\n", 
    "text": ""world"" 
} 

這應該表明問題是write_json

+0

你能提供一個鏈接到一個完整的工作示例來演示這個問題嗎? –

+0

你可以讓它成爲SSCCE嗎?我知道一個庫問題,導致問題解析/寫在主之外。你是從一個全局對象的析構函數中保存的嗎? – sehe

+0

好的。 (S)SCCE工作並顯示[一些令人困惑的輸出](http://paste.ubuntu.com/11241990/)。什麼是預期的輸出?因爲我在那裏看到的所有內容看起來都與我期望的相符 – sehe

回答

0

這可能是有史以來最愚蠢的錯誤:下跟蹤誤差的幾個星期後,我們發現,在Linux編譯我們用來在我們的機器上構建項目的shell腳本手動更改了JSON解析行爲。 如果我們從中學到了一些東西,它是:永遠不要相信你從互聯網複製的東西。

1

中在我的Linux機器我測試過這是不是一個問題:

Live On Coliru

#include<boost/property_tree/json_parser.hpp> 
#include<iostream> 

int main(){ 
    using namespace boost::property_tree; 

    ptree inner; 
    inner.put("area CHALLENGE_FUTURE.areaname CHALLENGE_FUTURE", "something"); 

    std::ostringstream inner_json; 
    write_json(inner_json, inner); 

    ptree outer; 
    outer.put("stations", inner_json.str()); 

    write_json(std::cout, outer); 
} 

它打印

{ 
    "stations": "{\n \"area CHALLENGE_FUTURE\": {\n  \"areaname CHALLENGE_FUTURE\": \"something\"\n }\n}\n" 
} 

正是它應該打印如果你問我。


如果你想讓'內部'JSON不被轉義,爲什麼不使它成爲同一棵樹的一部分呢?

Live On Coliru

#include<boost/property_tree/json_parser.hpp> 
#include<iostream> 

int main(){ 
    using namespace boost::property_tree; 

    ptree outer; 
    auto& inner = outer.add_child("stations", {}); 
    inner.put("area CHALLENGE_FUTURE.areaname CHALLENGE_FUTURE", "something"); 

    write_json(std::cout, outer); 
} 

它打印

{ 
    "stations": { 
     "area CHALLENGE_FUTURE": { 
      "areaname CHALLENGE_FUTURE": "something" 
     } 
    } 
}