该工具测试比jsoncpp,protobuf效率跟高。该工具主要有两个模块json主体结构和parser解析结构。知识点C++98/C++11/C++14,makefile等不需要使用第三方库支持跨平台。
1.json主体结构(json.h json.cc)
主要对常见的类型进行封装成json类
json.h
#pragma once
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <fstream>
#include <unordered_map>
using namespace std;
namespace yazi
{
namespace json
{
class Json
{
public:
typedef std::vector<Json>::iterator iterator;
iterator begin()
{
return _vaule._array->begin();
}
iterator end()
{
return _vaule._array->end();
}
enum Type
{
json_null = 0,
json_bool,
json_int,
json_double,
json_string,
json_array,
json_object
};
Json();
Json(bool vaule);
Json(int vaule);
Json(double vaule);
Json(const char* vaule);
Json(const std::string& vaule);
Json(Type type);
Json(const Json& other);
operator bool();
operator int();
operator double();
operator string();
Json& operator[](int index);
Json& operator[](const char* key);
Json& operator[](const std::string& key);
void operator=(const Json& other);
bool operator==(const Json& other);
bool operator!=(const Json& other);
//Json& operator*();
void append(const Json& other);
size_t size() const;
string str()const;
void copy(const Json& other);
void clear();
bool isNull()const { return _type == json_null; }
bool isBool()const { return _type == json_bool; }
bool isInt()const { return _type == json_int; }
bool isDouble()const { return _type == json_double; }
bool isString()const { return _type == json_string; }
bool isArray()const { return _type == json_array; }
bool isObject()const { return _type == json_object; }
bool asBool() const;
int asInt()const;
double asDouble()const;
std::string asString()const;
bool has(int index);
bool has(const char* key);
bool has(const std::string& key);
void remove(int index);
void remove(const char* key);
void remove(const std::string& key);
void parse(const std::string& str);
private:
union Vaule
{
bool _bool;
int _int;
double _double;
std::string* _string;
std::vector<Json>* _array;
std::map<std::string, Json>* _object;
};
Type _type;
Vaule _vaule;
};
}
}
Json.cc
#include "json.h"
#include "parser.h"
using namespace yazi::json;
Json::Json() : _type(json_null)
{
}
Json::Json(bool vaule) : _type(json_bool)
{
_vaule._bool = vaule;
}
Json::Json(int vaule) : _type(json_int)
{
_vaule._int = vaule;
}
Json::Json(double vaule) : _type(json_double)
{
_vaule._double = vaule;
}
Json::Json(const char* vaule) : _type(json_string)
{
_vaule._string = new std::string(vaule);
}
Json::Json(const std::string& vaule) : _type(json_string)
{
_vaule._string = new std::string(vaule);
}
Json::Json(Type type) : _type(type)
{
switch (_type)
{
case json_null:
break;
case json_bool:
_vaule._bool = false;
break;
case json_int:
_vaule._int = 0;
break;
case json_double:
_vaule._double = 0.0;
break;
case json_string:
_vaule._string = new std::string("");
break;
case json_array:
_vaule._array = new std::vector<Json>();
break;
case json_object:
_vaule._object = new std::map<std::string, Json>();
break;
default:
break;
}
}
Json::Json(const Json& other)
{
copy(other);
}
Json::operator bool()
{
if (_type != json_bool)
{
throw new logic_error("type error,not bool vaule");
}
return _vaule._bool;
}
Json::operator int()
{
if (_type != json_int)
{
throw new logic_error("type error,not int vaule");
}
return _vaule._int;
}
Json::operator double()
{
if (_type != json_double)
{
throw new logic_error("type error,not double vaule");
}
return _vaule._double;
}
Json::operator string()
{
if (_type != json_string)
{
throw new logic_error("type error,not string vaule");
}
return *(_vaule._string);
}
Json& Json::operator[](int index)
{
if (_type != json_array)
{
clear();
_type = json_array;
_vaule._array = new std::vector<Json>();
}
// if(index < 0)
// {
// throw new logic_error("array[] index < 0");
// }
size_t size = (_vaule._array)->size();
if (index >= size) // 扩容
{
for (size_t i = size; i <= index; ++i)
{
(_vaule._array)->push_back(Json());
}
}
return (_vaule._array)->at(index);
}
Json& Json::operator[](const char* key)
{
std::string name(key);
return (*(this))[name];
}
Json& Json::operator[](const std::string& key)
{
if (_type != json_object)
{
clear();
_type = json_object;
_vaule._object = new std::map<std::string, Json>();
}
return (*(_vaule._object))[key];
}
void Json::operator=(const Json& other)
{
clear();
copy(other);
}
void Json::append(const Json& other)
{
if (_type != json_array)
{
_type = json_array;
_vaule._array = new std::vector<Json>();
}
(_vaule._array)->push_back(other);
}
size_t Json::size() const
{
if (_type != json_array)
{
throw new logic_error("type error,not array vaule");
}
return (_vaule._array)->size();
}
string Json::str() const
{
std::stringstream ss;
switch (_type)
{
case json_null:
ss << "null";
break;
case json_bool:
if (_vaule._bool)
{
ss << "true";
}
else
{
ss << "false";
}
break;
case json_int:
ss << _vaule._int;
break;
case json_double:
ss << _vaule._double;
break;
case json_string:
ss << '\"' << *(_vaule._string) << '\"';
break;
case json_array:
{
ss << '[';
for (auto it = (_vaule._array)->begin(); it != (_vaule._array)->end(); ++it)
{
if (it != (_vaule._array)->begin())
{
ss << ',';
}
ss << it->str();
}
ss << ']';
break;
}
case json_object:
{
ss << '{';
for (auto it = (_vaule._object)->begin(); it != (_vaule._object)->end(); ++it)
{
if (it != (_vaule._object)->begin())
{
ss << ',';
}
ss << '\"' << it->first << '\"' << ':' << it->second.str();
}
ss << '}';
break;
}
default:
break;
}
return ss.str();
}
void Json::copy(const Json& other)
{
_type = other._type;
switch (_type)
{
case json_null:
break;
case json_bool:
_vaule._bool = other._vaule._bool;
break;
case json_int:
_vaule._int = other._vaule._int;
break;
case json_double:
_vaule._double = other._vaule._double;
break;
case json_string:
_vaule._string = other._vaule._string;
break;
case json_array:
_vaule._array = other._vaule._array;
break;
case json_object:
_vaule._object = other._vaule._object;
break;
default:
break;
}
}
void Json::clear()
{
switch (_type)
{
case json_null:
break;
case json_bool:
_vaule._bool = false;
break;
case json_int:
_vaule._int = 0;
break;
case json_double:
_vaule._double = 0.0;
break;
case json_string:
delete _vaule._string;
break;
case json_array:
{
for (auto it = _vaule._array->begin(); it != _vaule._array->end(); ++it)
{
it->clear();
}
delete _vaule._array;
break;
}
case json_object:
{
for (auto it = _vaule._object->begin(); it != _vaule._object->end(); ++it)
{
(it->second).clear();
}
delete _vaule._object;
break;
}
default:
break;
}
_type = json_null;
}
bool Json::operator==(const Json& other) //浅拷贝(效率)
{
if (_type != other._type)
{
return false;
}
switch (_type)
{
case json_null:
return true;
case json_bool:
return _vaule._bool == other._vaule._bool;
case json_int:
return _vaule._int == other._vaule._int;
case json_double:
return _vaule._double == other._vaule._double;
case json_string:
return *(_vaule._string) == *(other._vaule._string);
case json_array:
{
/*if(_vaule._array->size() != other._vaule._array->size())
{
return false;
}
for (size_t i = 0; i < _vaule._array->size(); ++i)
{
if ((*(_vaule._array))[i] != (* (other._vaule._array))[i])
{
return false;
}
}
return true;*/
return _vaule._array == other._vaule._array;
}
case json_object:
{
return _vaule._object == other._vaule._object;
}
default:
break;
}
return false;
}
bool Json::operator!=(const Json& other)
{
return !(*this == other);
}
bool Json::asBool() const
{
if (_type != json_bool)
{
throw new std::logic_error("type error,not bool vaule");
}
return _vaule._bool;
}
int Json::asInt()const
{
if (_type != json_int)
{
throw new std::logic_error("type error,not int vaule");
}
return _vaule._int;
}
double Json::asDouble()const
{
if (_type != json_double)
{
throw new std::logic_error("type error,not double vaule");
}
return _vaule._double;
}
std::string Json::asString()const
{
if (_type != json_string)
{
throw new std::logic_error("type error,not string vaule");
}
return *(_vaule._string);
}
bool Json::has(int index)
{
if (_type != json_array)
{
return false;
}
int size = static_cast<int>((_vaule._array)->size());
return (index >= 0 && index < size);
}
bool Json::has(const char* key)
{
string name(key);
return has(name);
}
bool Json::has(const std::string& key)
{
if (_type != json_object)
{
return false;
}
return (_vaule._object)->find(key) != (_vaule._object)->end();
}
void Json::remove(int index)
{
if (_type != json_array)
{
return;
}
int size = static_cast<int>((_vaule._array)->size());
if (index < 0 || index >= size)
{
return;
}
(_vaule._array)->at(index).clear();
(_vaule._array)->erase((_vaule._array)->begin() + index);
}
void Json::remove(const char* key)
{
string name(key);
return remove(name);
}
void Json::remove(const std::string& key)
{
auto it = (_vaule._object)->find(key);
if (it == (_vaule._object)->end())
{
return;
}
(*(_vaule._object))[key].clear();
(_vaule._object)->erase(key);
}
void Json::parse(const std::string& str)
{
Parser p;
p.load(str);
*this = p.parse();
}
2.parser模块对json串进行解析
parser模块中用递归的方式处理。
parser.h
#pragma once
#include "json.h"
#include <string>
#include <regex>
namespace yazi
{
namespace json
{
class Parser
{
public:
Parser();
void load(const std::string& str);
Json parse();
private:
void skip_white_space();
char get_next_token();
Json parse_null();
Json parse_bool();
Json parse_number();
std::string parse_string();
Json parse_array();
Json parse_object();
private:
std::string _str;
int _index;
};
}
}
parser.cc
#include "parser.h"
using namespace yazi::json;
Parser::Parser() : _str(""), _index(0)
{
}
void Parser::load(const std::string& str)
{
_str = str;
_index = 0;
}
Json Parser::parse()
{
char ch = get_next_token();
switch (ch)
{
case 'n':
--_index;
return parse_null();
case 't':
case 'f':
--_index;
return parse_bool();
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
--_index;
return parse_number();
case '"':
return Json(parse_string());
case '[':
return parse_array();
case '{':
return parse_object();
default:
break;
}
throw new std::logic_error("unexpected char");
}
void Parser::skip_white_space()
{
while (_str[_index] == ' ' || _str[_index] == '\n' || _str[_index] == '\r' || _str[_index] == '\t')
{
++_index;
}
}
char Parser::get_next_token()
{
skip_white_space();
return _str[_index++];
}
Json Parser::parse_null()
{
if (_str.compare(_index, 4, "null") == 0)
{
_index += 4;
return Json();
}
throw new std::logic_error("parse null error");
}
Json Parser::parse_bool()
{
if (_str.compare(_index, 4, "true") == 0)
{
_index += 4;
return Json(true);
}
else if (_str.compare(_index, 5, "false") == 0)
{
_index += 5;
return Json(false);
}
else
{
throw new std::logic_error("parse bool error");
}
}
Json Parser::parse_number()
{
int pos = _index;
if (_str[_index] == '-')
{
++_index;
}
if (isdigit(_str[_index]) == 0)
{
throw new std::logic_error("parse number error");
}
while (isdigit(_str[_index]) != 0)
{
++_index;
}
if (_str[_index] != '.')
{
int i = std::atoi(_str.c_str() + pos);
return Json(std::move(i));
}
if (isdigit(_str[++_index]) == 0)
{
throw new std::logic_error("parse number error");
}
while (isdigit(_str[_index]) != 0)
{
++_index;
}
double f = std::atof(_str.c_str() + pos);
return Json(std::move(f));
}
std::string Parser::parse_string()
{
std::string out;
while (true)
{
char ch = _str[_index++];
if (ch == '"')
{
break;
}
if (ch == '\\')
{
ch = _str[_index++];
switch (ch)
{
case '\n':
out += '\n';
break;
case '\r':
out += '\r';
break;
case '\t':
out += '\t';
break;
case '\b':
out += '\b';
break;
case '\f':
out += '\f';
break;
case '"':
out += "\\\"";
break;
case '\\':
out += "\\\\";
break;
case 'u':
out += "\\u";
for (int i = 0; i < 4; ++i)
{
out += _str[_index++];
}
break;
default:
break;
}
}
else
{
out += ch;
}
}
return std::move(out);
}
Json Parser::parse_array()
{
Json arr(Json::json_array);
char ch = get_next_token();
if (ch == ']')
{
return arr;
}
--_index;
while (true)
{
arr.append(parse()); //递归解析
ch = get_next_token();
if (ch == ']')
{
break;
}
if (ch != ',')
{
throw new std::logic_error("parse array error");
}
//++_index;//ERROR
}
return arr;
}
Json Parser::parse_object()
{
Json obj(Json::json_object);
char ch = get_next_token();
if(ch == '}')
{
return obj;
}
--_index;
while(true)
{
ch = get_next_token();
if(ch != '"')
{
throw new std::logic_error("parse object error");
}
std::string key = parse_string();
ch = get_next_token();
if(ch != ':')
{
throw new std::logic_error("parse object error");
}
obj[key] = parse();
ch = get_next_token();
if(ch == '}')
{
break;
}
if(ch != ',')
{
throw new std::logic_error("parse object error");
}
//++_index;//ERROR
}
return obj;
}
main.cc
#include <iostream>
using namespace std;
#include "json/json.h"
using namespace yazi::json;
#include <fstream>
void Test1(void)
{
const std::string& str = "[1, \"a\", 3, null, -1.2345, true, false, \"hello world\"]";
Json v;
v.parse(str);
std::cout << v.str() << std::endl;
}
void Test2(void)
{
const std::string& str = "{\"a\":true,\"b\":{\"a\":true,\"b\":[1, \"a\", 3, null, -1.2345, true, false, \"hello world\"],\"c\":123,\"4\":\"hello world\"},\"c\":123,\"4\":\"hello world\"}";
Json v;
v.parse(str);
std::cout << v.str() << std::endl;
}
void Test3()
{
ifstream in("./json_test");
stringstream ss;
ss << in.rdbuf();
const std::string& str = ss.str();
//std::cout << str << std::endl;
Json v;
v.parse(str);
//std::cout << v.str() << std::endl;
std::cout << v["data"]["search_data"][0]["elements"][0]["name"].str() << std::endl;
}
int main()
{
Test3();
//Test2();
//Test1();
// Json v1;
// Json v2 = true;
// Json v3 = 123;
// Json v4 = 1.23;
// Json v5 = "hello";
// bool b = v2;
// int i = v3;
// double f = v4;
// const std::string& s = v5;
// cout << b << " " << i <<" " << f << " " << s << endl;
// Json arr;
// arr[1] = true;
// arr[2] = 123;
// arr[3] = 1.23;
// arr[4] = "hello";
// /*std::cout << arr.has(0) << std::endl;
// std::cout << arr.has(4) << std::endl;*/
// Json obj;
// obj["bool"] = true;
// obj["int"] = 123;
// obj["double"] = 1.23;
// obj["arr"] = arr;
// std::cout << obj.str() << std::endl;
// obj.clear();
}
makefile
main:main.cc ./json/json.cc ./json/parser.cc
g++ -o $@ $^ -std=c++14
.PHONY:clean
clean:
rm -rf main
json_test(用来测试)
{
"status": 0,
"message": "",
"data": {
"search_data": [
{
"elements": [
{
"rating": 0,
"name": "奈良市",
"url": "/scenic/3/10052/",
"wish_to_go_count": 328,
"name_orig": "奈良市",
"visited_count": 1958,
"comments_count": 0,
"location": {
"lat": 34.685087,
"lng": 135.805
},
"has_experience": false,
"rating_users": 0,
"name_zh": "奈良市",
"name_en": "Nara",
"type": 3,
"id": 10052,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "小樽市",
"url": "/scenic/3/26772/",
"wish_to_go_count": 266,
"name_orig": "小樽市",
"visited_count": 954,
"comments_count": 0,
"location": {
"lat": 43.190717,
"lng": 140.994662
},
"has_experience": false,
"rating_users": 0,
"name_zh": "小樽市",
"name_en": "Otaru",
"type": 3,
"id": 26772,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "槟城",
"url": "/scenic/2/8257/",
"wish_to_go_count": 93,
"name_orig": "槟城",
"visited_count": 849,
"comments_count": 0,
"location": {
"lat": 5.414167,
"lng": 100.328759
},
"has_experience": false,
"rating_users": 0,
"name_zh": "槟城",
"name_en": "Penang",
"type": 2,
"id": 8257,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/province.png"
},
{
"rating": 0,
"name": "墨尔本",
"url": "/scenic/3/47810/",
"wish_to_go_count": 2927,
"name_orig": "墨尔本",
"visited_count": 2112,
"comments_count": 0,
"location": {
"lat": -37.814216,
"lng": 144.963231
},
"has_experience": false,
"rating_users": 0,
"name_zh": "墨尔本",
"name_en": "Melbourne",
"type": 3,
"id": 47810,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "广岛县",
"url": "/scenic/2/9474/",
"wish_to_go_count": 36,
"name_orig": "广岛县",
"visited_count": 160,
"comments_count": 0,
"location": {
"lat": 34.39656,
"lng": 132.459622
},
"has_experience": false,
"rating_users": 0,
"name_zh": "广岛县",
"name_en": "Hiroshima Prefecture",
"type": 2,
"id": 9474,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/province.png"
},
{
"rating": 0,
"name": "泰国",
"url": "/scenic/1/3649/",
"wish_to_go_count": 22131,
"name_orig": "泰国",
"visited_count": 22298,
"comments_count": 0,
"location": {
"lat": 15.870032,
"lng": 100.992541
},
"has_experience": false,
"icon": "http://media.breadtrip.com/images/icons/2/country.png",
"type": 1,
"id": "TH",
"has_route_maps": false,
"rating_users": 0
},
{
"rating": 0,
"name": "芬兰",
"url": "/scenic/1/3613/",
"wish_to_go_count": 665,
"name_orig": "芬兰",
"visited_count": 1058,
"comments_count": 0,
"location": {
"lat": 61.92411,
"lng": 25.748151
},
"has_experience": false,
"icon": "http://media.breadtrip.com/images/icons/2/country.png",
"type": 1,
"id": "FI",
"has_route_maps": false,
"rating_users": 0
},
{
"rating": 0,
"name": "美国",
"url": "/scenic/1/3803/",
"wish_to_go_count": 8828,
"name_orig": "美国",
"visited_count": 12967,
"comments_count": 0,
"location": {
"lat": 37.09024,
"lng": -95.712891
},
"has_experience": false,
"icon": "http://media.breadtrip.com/images/icons/2/country.png",
"type": 1,
"id": "US",
"has_route_maps": false,
"rating_users": 0
},
{
"rating": 0,
"name": "马来西亚",
"url": "/scenic/1/3676/",
"wish_to_go_count": 6339,
"name_orig": "马来西亚",
"visited_count": 9533,
"comments_count": 0,
"location": {
"lat": 4.210484,
"lng": 101.975766
},
"has_experience": false,
"icon": "http://media.breadtrip.com/images/icons/2/country.png",
"type": 1,
"id": "MY",
"has_route_maps": false,
"rating_users": 0
},
{
"rating": 0,
"name": "意大利",
"url": "/scenic/1/3720/",
"wish_to_go_count": 7689,
"name_orig": "意大利",
"visited_count": 6508,
"comments_count": 0,
"location": {
"lat": 41.87194,
"lng": 12.56738
},
"has_experience": false,
"icon": "http://media.breadtrip.com/images/icons/2/country.png",
"type": 1,
"id": "IT",
"has_route_maps": false,
"rating_users": 0
},
{
"rating": 0,
"name": "新加坡",
"url": "/scenic/1/3589/",
"wish_to_go_count": 5847,
"name_orig": "新加坡",
"visited_count": 7835,
"comments_count": 0,
"location": {
"lat": 1.352083,
"lng": 103.819836
},
"has_experience": false,
"icon": "http://media.breadtrip.com/images/icons/2/country.png",
"type": 1,
"id": "SG",
"has_route_maps": false,
"rating_users": 0
},
{
"rating": 0,
"name": "挪威",
"url": "/scenic/1/3258/",
"wish_to_go_count": 788,
"name_orig": "挪威",
"visited_count": 850,
"comments_count": 0,
"location": {
"lat": 60.472024,
"lng": 8.468946
},
"has_experience": false,
"icon": "http://media.breadtrip.com/images/icons/2/country.png",
"type": 1,
"id": "NO",
"has_route_maps": false,
"rating_users": 0
}
],
"type": "destination",
"title": "国外热门目的地"
},
{
"elements": [
{
"rating": 0,
"name": "台湾",
"url": "/scenic/1/3660/",
"wish_to_go_count": 37947,
"name_orig": "台湾",
"visited_count": 15729,
"comments_count": 0,
"location": {
"lat": 23.69781,
"lng": 120.960515
},
"has_experience": false,
"icon": "http://media.breadtrip.com/images/icons/2/country.png",
"type": 1,
"id": "TW",
"has_route_maps": false,
"rating_users": 0
},
{
"rating": 0,
"name": "香港",
"url": "/scenic/1/3814/",
"wish_to_go_count": 23495,
"name_orig": "香港",
"visited_count": 31249,
"comments_count": 0,
"location": {
"lat": 22.396428,
"lng": 114.109497
},
"has_experience": false,
"icon": "http://media.breadtrip.com/images/icons/2/country.png",
"type": 1,
"id": "HK",
"has_route_maps": true,
"rating_users": 0
},
{
"rating": 0,
"name": "厦门",
"url": "/scenic/3/65012/",
"wish_to_go_count": 29887,
"name_orig": "厦门",
"visited_count": 26077,
"comments_count": 0,
"location": {
"lat": 24.477188,
"lng": 118.094398
},
"has_experience": false,
"rating_users": 0,
"name_zh": "厦门",
"name_en": "Xiamen",
"type": 3,
"id": 65012,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "北京",
"url": "/scenic/3/8248/",
"wish_to_go_count": 7118,
"name_orig": "北京",
"visited_count": 53416,
"comments_count": 0,
"location": {
"lat": 39.90561,
"lng": 116.413634
},
"has_experience": false,
"rating_users": 0,
"name_zh": "北京",
"name_en": "Beijing",
"type": 3,
"id": 8248,
"has_route_maps": true,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "丽江市",
"url": "/scenic/3/65362/",
"wish_to_go_count": 27368,
"name_orig": "丽江市",
"visited_count": 19389,
"comments_count": 0,
"location": {
"lat": 26.851553,
"lng": 100.228931
},
"has_experience": false,
"rating_users": 0,
"name_zh": "丽江市",
"name_en": "Lijiang",
"type": 3,
"id": 65362,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "成都",
"url": "/scenic/3/14209/",
"wish_to_go_count": 14464,
"name_orig": "成都",
"visited_count": 23484,
"comments_count": 0,
"location": {
"lat": 30.569858,
"lng": 104.069084
},
"has_experience": false,
"rating_users": 0,
"name_zh": "成都",
"name_en": "Chengdu",
"type": 3,
"id": 14209,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "上海",
"url": "/scenic/3/13961/",
"wish_to_go_count": 7601,
"name_orig": "上海",
"visited_count": 46500,
"comments_count": 0,
"location": {
"lat": 31.228402,
"lng": 121.478143
},
"has_experience": false,
"rating_users": 0,
"name_zh": "上海",
"name_en": "Shanghai",
"type": 3,
"id": 13961,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "拉萨",
"url": "/scenic/3/5249/",
"wish_to_go_count": 15492,
"name_orig": "拉萨",
"visited_count": 6544,
"comments_count": 0,
"location": {
"lat": 29.649671,
"lng": 91.173526
},
"has_experience": false,
"rating_users": 0,
"name_zh": "拉萨",
"name_en": "Lhasa",
"type": 3,
"id": 5249,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "大理",
"url": "/scenic/3/43908/",
"wish_to_go_count": 10103,
"name_orig": "大理",
"visited_count": 13291,
"comments_count": 0,
"location": {
"lat": 25.603496,
"lng": 100.268781
},
"has_experience": false,
"rating_users": 0,
"name_zh": "大理",
"name_en": "Dali",
"type": 3,
"id": 43908,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
},
{
"rating": 0,
"name": "三亚",
"url": "/scenic/3/65557/",
"wish_to_go_count": 8951,
"name_orig": "三亚",
"visited_count": 11920,
"comments_count": 0,
"location": {
"lat": 18.251176,
"lng": 109.51604
},
"has_experience": false,
"rating_users": 0,
"name_zh": "三亚",
"name_en": "Sanya",
"type": 3,
"id": 65557,
"has_route_maps": false,
"icon": "http://media.breadtrip.com/images/icons/2/city.png"
}
],
"type": "destination",
"title": "国内热门目的地"
}
],
"date_time": "2017-09-11 10:52:27.811321",
"elements": [
{
"type": 1,
"data": [
[
{
"platform": "android",
"image_url": "http://photos.breadtrip.com/covers_2017_09_05_d5b225291045767fcfa0508f3d96ae26.jpg?imageView/2/w/960/",
"html_url": "http://www.iqiyi.com/v_19rr8w7drg.html?dummy=&wx_uid2=wxidoG0a9jsIGfq1jln1HtWebMCOKKSQ"
},
{
"platform": "android",
"image_url": "http://photos.breadtrip.com/covers_2014_12_25_ed43331bac65ee0752f7e56116993b2c.jpg?imageView2/2/w/750/format/jpg/interlace/1/",
"html_url": "http://web.breadtrip.com/mobile/destination/topic/2387718817/"
},
{
"platform": "android",
"image_url": "http://photos.breadtrip.com/covers_2014_10_29_46217a91ace672787ed1fec4c2011b52.png?imageView2/2/w/750/format/jpg/interlace/1/",
"html_url": "http://web.breadtrip.com/mobile/destination/topic/2387718790/"
},
{
"platform": "android",
"image_url": "http://photos.breadtrip.com/covers_2015_04_24_59b10571419fb3888224d83365f561e8.jpg?imageView2/2/w/750/format/jpg/interlace/1/",
"html_url": "http://web.breadtrip.com/mobile/destination/topic/2387718734/"
},
{
"platform": "android",
"image_url": "http://photos.breadtrip.com/covers_2014_12_05_c9aea564f43b673ea6d8dcf9c6c4627b.jpg?imageView2/2/w/750/format/jpg/interlace/1/",
"html_url": "http://web.breadtrip.com/mobile/destination/topic/2387718810/"
},
{
"platform": "android",
"image_url": "http://photos.breadtrip.com/covers_2016_02_26_51987e2bebba67bad75ccb114dfea7ab.png?imageView2/2/w/750/format/jpg/interlace/1/",
"html_url": "http://web.breadtrip.com/mobile/destination/topic/2387719110/"
}
]
],
"desc": "广告banner"
},
{
"type": 11,
"data": [
{
"title": "每日精选故事"
}
],
"desc": ""
},
{
"type": 10,
"data": [
{
"text": "halo 雷猴嘛\n我猜你是个妹纸\n我猜你是个爱美的妹纸\n我猜你是个喜欢首饰and爱美的妹纸\n我猜你是个喜欢手作首饰and爱美的妹纸\n我猜…你是个亲手做首饰给女票的汉纸!\n\n\n\n我是个喜欢手工喜欢首饰喜欢发现美的美男纸,来吃我一安利呗!",
"is_liked": false,
"index_cover": "http://photos.breadtrip.com/photo_d_2016_08_02_dca0659576dfe60382943612e2fff308ff2b1f1d56055e6b29f6fdd317f3ff8c.jpeg?imageView/2/w/960/q/85",
"poi": {},
"cover_image_height": 1206,
"trip_id": 2387270842,
"index_title": "",
"center_point": {
"lat": 0,
"lng": 0
},
"view_count": 45446,
"location_alias": "Dream High梦想社",
"cover_image_1600": "http://photos.breadtrip.com/photo_d_2016_08_02_09931d012facf6186f9c25202983dcaa68efddaccb4cbc0e78a0ded46afb778e.jpeg?imageView/2/w/1384/h/1384/q/85",
"cover_image_s": "http://photos.breadtrip.com/photo_d_2016_08_02_09931d012facf6186f9c25202983dcaa68efddaccb4cbc0e78a0ded46afb778e.jpeg?imageView/1/w/280/h/280/q/75",
"share_url": "btrip/spot/2387867495/",
"timezone": "Asia/Shanghai",
"date_tour": "2016-07-31T20:49:00+08:00",
"is_hiding_location": true,
"user": {
"location_name": "广东_广州",
"name": "林酷儿",
"resident_city_id": 275,
"mobile": "",
"gender": 2,
"avatar_m": "http://photos.breadtrip.com/avatar_17_c1_1214298920959e94700b4cb370271320c5745e6c.jpg-avatar.m",
"cover": "http://photos.breadtrip.com/default_user_cover_06.jpg-usercover.display",
"custom_url": "",
"experience": {
"value": 101,
"level_info": {
"name": "",
"value": 2
}
},
"id": 2384430044,
"birthday": "",
"country_num": null,
"avatar_s": "http://photos.breadtrip.com/avatar_17_c1_1214298920959e94700b4cb370271320c5745e6c.jpg-avatar.s",
"country_code": null,
"email_verified": false,
"is_hunter": false,
"cdc2": false,
"avatar_l": "http://photos.breadtrip.com/avatar_17_c1_1214298920959e94700b4cb370271320c5745e6c.jpg-avatar.l",
"email": "",
"user_desc": "",
"points": 26
},
"spot_id": 2387867495,
"is_author": false,
"cover_image_w640": "http://photos.breadtrip.com/photo_d_2016_08_02_09931d012facf6186f9c25202983dcaa68efddaccb4cbc0e78a0ded46afb778e.jpeg?imageView/1/w/640/h/480/q/85",
"region": {
"primary": "",
"secondary": ""
},
"comments_count": 9,
"cover_image": "http://photos.breadtrip.com/photo_d_2016_08_02_09931d012facf6186f9c25202983dcaa68efddaccb4cbc0e78a0ded46afb778e.jpeg?imageView/2/w/960/q/85",
"cover_image_width": 1600,
"recommendations_count": 22
}
],
"desc": ""
},
{
"type": 10,
"data": [
{
"text": "挑战极限和心跳,让自己成为一个有趣的人。\n2016月5月在面包旅行,偶然下兼职做一个猎人哈哈(活动名字上帝之眼),感觉挺酷的。\n重庆最具特色的,当然是城市里最耀眼的高楼夜景,曾经有一部电影“重庆森林”,故事很多场景,以及城市展现出重庆这座奇葩一样的城市,独具特色话3D话,很多电影都有在重庆取景。",
"is_liked": false,
"index_cover": "http://photos.breadtrip.com/photo_d_2016_06_24_4bc9f811fa5b900762fb1d55dff164f32066a28d4010bf9f4a4c20dd62634b4d.jpg?imageView/2/w/960/q/85",
"poi": {
"tel": "023-62872299",
"currency": "CNY",
"is_nearby": true,
"timezone": "Asia/Chongqing",
"id": 2387458620,
"category": 11,
"recommended_reason": "看滚滚江水向东流去,犹如有登高望远",
"fee": "免费",
"spot_region": "重庆",
"date_added": "2014-12-16 10:48:55",
"time_consuming_max": 0,
"time_consuming": null,
"extra1": "",
"recommended": true,
"location": {
"lat": 29.554926,
"lng": 106.586696
},
"opening_time": "全天",
"type": 5,
"time_consuming_min": 0,
"website": "",
"description": "长江索道起于渝中区长安寺,横跨长江至南岸区上新街。乘坐过江索道,除了可以欣赏两江美景外,还可以从索道上俯瞰洪崖洞、湖广会馆、南滨路等著名景观。",
"arrival_type": "乘105、132、135、153、181、261、0321夜班、476、0491夜班、0492夜班、0493夜班、871、898路等公交车在新华路站下车即到",
"address": "重庆市渝中区新华路153号",
"verified": true,
"name_en": "",
"icon": "http://media.breadtrip.com/images/icons/poi_category_11.png",
"name": "长江索道",
"popularity": 809
},
"cover_image_height": 1067,
"trip_id": 2387276098,
"index_title": "",
"center_point": {
"lat": 0,
"lng": 0
},
"view_count": 49744,
"location_alias": "",
"cover_image_1600": "http://photos.breadtrip.com/photo_d_2016_06_24_7df54edb43492917e7d1cb743a4a379b92bc85c2562bb6b1a39d346c4f6fc0e9.jpg?imageView/2/w/1384/h/1384/q/85",
"cover_image_s": "http://photos.breadtrip.com/photo_d_2016_06_24_7df54edb43492917e7d1cb743a4a379b92bc85c2562bb6b1a39d346c4f6fc0e9.jpg?imageView/1/w/280/h/280/q/75",
"share_url": "btrip/spot/2387843168/",
"timezone": "Asia/Shanghai",
"date_tour": "2016-06-24T10:51:14+08:00",
"is_hiding_location": false,
"user": {
"location_name": "",
"name": "W猫小北",
"resident_city_id": 288652,
"mobile": "",
"gender": 1,
"avatar_m": "http://photos.breadtrip.com/avatar_bc_fd_a6720f5e50d1f22194c51f73329f0e9c48ab7ba8.jpg-avatar.m",
"cover": "http://photos.breadtrip.com/default_user_cover_05.jpg-usercover.display",
"custom_url": "",
"experience": {
"value": 1242,
"level_info": {
"name": "",
"value": 4
}
},
"id": 2387577484,
"birthday": "",
"country_num": "86",
"avatar_s": "http://photos.breadtrip.com/avatar_bc_fd_a6720f5e50d1f22194c51f73329f0e9c48ab7ba8.jpg-avatar.s",
"country_code": "CN",
"email_verified": false,
"is_hunter": true,
"cdc2": false,
"avatar_l": "http://photos.breadtrip.com/avatar_bc_fd_a6720f5e50d1f22194c51f73329f0e9c48ab7ba8.jpg-avatar.l",
"email": "",
"user_desc": "我是猫小北,喜欢摄影摄像以及美食旅行,独自旅行大半中国,特长就是长得帅,吃得多还不胖。喜欢用心记录不容易发现的美好,我想记录这一切的美好。",
"points": 87
},
"spot_id": 2387843168,
"is_author": false,
"cover_image_w640": "http://photos.breadtrip.com/photo_d_2016_06_24_7df54edb43492917e7d1cb743a4a379b92bc85c2562bb6b1a39d346c4f6fc0e9.jpg?imageView/1/w/640/h/480/q/85",
"region": {
"primary": "",
"secondary": ""
},
"comments_count": 7,
"cover_image": "http://photos.breadtrip.com/photo_d_2016_06_24_7df54edb43492917e7d1cb743a4a379b92bc85c2562bb6b1a39d346c4f6fc0e9.jpg?imageView/2/w/960/q/85",
"cover_image_width": 1600,
"recommendations_count": 40
}
],
"desc": ""
},
{
"type": 10,
"data": [
{
"text": "很有幸能有王老这样的人让我们能睹旧物思旧人旧事,陈列室虽不大,却也充斥了老北京饮食医用居的方方面面,可以亲手触摸历史,了解自己现在工作城市的过去现在让人倍感亲切!",
"is_liked": false,
"index_cover": "http://photos.breadtrip.com/photo_d_2016_06_09_23_56_51_174_123986672_-1220261189.jpg?imageView/2/w/960/q/85",
"poi": "",
"cover_image_height": 724,
"trip_id": 2387247307,
"index_title": "",
"center_point": {},
"view_count": 39166,
"location_alias": "",
"cover_image_1600": "http://photos.breadtrip.com/photo_d_2016_06_09_23_56_50_703_123986672_834948024.jpg?imageView/2/w/1384/h/1384/q/85",
"cover_image_s": "http://photos.breadtrip.com/photo_d_2016_06_09_23_56_50_703_123986672_834948024.jpg?imageView/1/w/280/h/280/q/75",
"share_url": "btrip/spot/2387849028/",
"timezone": "Asia/Shanghai",
"date_tour": "2016-06-09T23:42:30+08:00",
"is_hiding_location": false,
"user": {
"location_name": "",
"name": "Lucy鱼er",
"resident_city_id": "",
"mobile": "",
"gender": 2,
"avatar_m": "http://photos.breadtrip.com/avatar_d0_f2_9450f59011820dcdfe7f0317d76f7fb0654cf52c.jpg-avatar.m",
"cover": "http://media.breadtrip.com/user_covers/default/cover_3.jpg",
"custom_url": "",
"experience": {
"value": 132,
"level_info": {
"name": "",
"value": 2
}
},
"id": 2386840189,
"birthday": "",
"country_num": null,
"avatar_s": "http://photos.breadtrip.com/avatar_d0_f2_9450f59011820dcdfe7f0317d76f7fb0654cf52c.jpg-avatar.s",
"country_code": null,
"email_verified": false,
"is_hunter": false,
"cdc2": false,
"avatar_l": "http://photos.breadtrip.com/avatar_d0_f2_9450f59011820dcdfe7f0317d76f7fb0654cf52c.jpg-avatar.l",
"email": "",
"user_desc": "",
"points": 52
},
"spot_id": 2387849028,
"is_author": false,
"cover_image_w640": "http://photos.breadtrip.com/photo_d_2016_06_09_23_56_50_703_123986672_834948024.jpg?imageView/1/w/640/h/480/q/85",
"region": {
"primary": "",
"secondary": ""
},
"comments_count": 2,
"cover_image": "http://photos.breadtrip.com/photo_d_2016_06_09_23_56_50_703_123986672_834948024.jpg?imageView/2/w/960/q/85",
"cover_image_width": 965,
"recommendations_count": 13
}
],
"desc": ""
},
{
"type": 10,
"data": [
{
"text": "献给爱我们的女神",
"is_liked": false,
"index_cover": "http://photos.breadtrip.com/photo_d_2016_06_19_01_21_20_989_123986672_17737936936133063098.jpg?imageView/2/w/960/q/85",
"poi": "",
"cover_image_height": 816,
"trip_id": 2387282916,
"index_title": "",
"center_point": {},
"view_count": 36207,
"location_alias": "",
"cover_image_1600": "http://photos.breadtrip.com/photo_d_2016_06_19_01_21_20_926_123986672_17737936923172662193.jpg?imageView/2/w/1384/h/1384/q/85",
"cover_image_s": "http://photos.breadtrip.com/photo_d_2016_06_19_01_21_20_926_123986672_17737936923172662193.jpg?imageView/1/w/280/h/280/q/75",
"share_url": "btrip/spot/2387842143/",
"timezone": "Asia/Shanghai",
"date_tour": "2016-06-19T01:19:07+08:00",
"is_hiding_location": false,
"user": {
"location_name": "",
"name": "丑到没墙角",
"resident_city_id": "",
"mobile": "",
"gender": 2,
"avatar_m": "http://photos.breadtrip.com/avatar_41_b8_aedfd71640e3ec09d0c30edc47df04dc56dbf38a.jpg-avatar.m",
"cover": "http://photos.breadtrip.com/default_user_cover_10.jpg-usercover.display",
"custom_url": "",
"experience": {
"value": 59,
"level_info": {
"name": "",
"value": 1
}
},
"id": 2384288641,
"birthday": "",
"country_num": null,
"avatar_s": "http://photos.breadtrip.com/avatar_41_b8_aedfd71640e3ec09d0c30edc47df04dc56dbf38a.jpg-avatar.s",
"country_code": null,
"email_verified": false,
"is_hunter": false,
"cdc2": false,
"avatar_l": "http://photos.breadtrip.com/avatar_41_b8_aedfd71640e3ec09d0c30edc47df04dc56dbf38a.jpg-avatar.l",
"email": "",
"user_desc": "",
"points": 2
},
"spot_id": 2387842143,
"is_author": false,
"cover_image_w640": "http://photos.breadtrip.com/photo_d_2016_06_19_01_21_20_926_123986672_17737936923172662193.jpg?imageView/1/w/640/h/480/q/85",
"region": {
"primary": "",
"secondary": ""
},
"comments_count": 2,
"cover_image": "http://photos.breadtrip.com/photo_d_2016_06_19_01_21_20_926_123986672_17737936923172662193.jpg?imageView/2/w/960/q/85",
"cover_image_width": 1088,
"recommendations_count": 21
}
],
"desc": ""
},
{
"type": 9,
"data": [
{
"title": "精彩原创和专题"
}
],
"desc": ""
},
{
"type": 4,
"data": [
{
"cover_image_default": "http://photos.breadtrip.com/photo_2017_07_18_0996aaecaadfdc9ed3534ed9b0c4928c.jpg?imageView/2/w/960/q/85",
"waypoints": 153,
"wifi_sync": false,
"last_day": "2017-06-14",
"id": 2387425140,
"view_count": 25191,
"privacy": 0,
"day_count": 7,
"index_title": "荷兰--黄金时代的回响"
}
]
}
]
}
}
相关测试截图: