aboutsummaryrefslogtreecommitdiff
path: root/cpp/src/util/json.cc
blob: 352c62b8e4464b0c6b39adafc96f7052d24dfb3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// Copyright (C) 2013 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "json.h"

#include <libaddressinput/util/basictypes.h>
#include <libaddressinput/util/scoped_ptr.h>

#include <cassert>
#include <cstddef>
#include <map>
#include <string>
#include <utility>
#include <vector>

#include <rapidjson/document.h>
#include <rapidjson/reader.h>

namespace i18n {
namespace addressinput {

using rapidjson::Document;
using rapidjson::kParseValidateEncodingFlag;
using rapidjson::Value;

class Json::JsonImpl {
 public:
  // Takes ownership of |document|.
  explicit JsonImpl(const Document* document)
      : document_(document), value_(document), dictionaries_() {
    assert(value_ != NULL);
    assert(value_->IsObject());
    BuildKeyList();
  }

  // Does not take ownership of |value|.
  explicit JsonImpl(const Value* value)
      : document_(), value_(value), dictionaries_() {
    assert(value_ != NULL);
    assert(value_->IsObject());
    BuildKeyList();
  }

  ~JsonImpl() {
    for (std::map<std::string, const Json*>::const_iterator
         it = dictionaries_.begin();
         it != dictionaries_.end(); ++it) {
      delete it->second;
    }
  }

  // The caller does not own the result.
  const Value::Member* FindMember(const std::string& key) {
    return value_->FindMember(key.c_str());
  }

  // The caller does not own the result. The result can be NULL if there's no
  // dictionary for |key|.
  const Json* FindDictionary(const std::string& key) const {
    std::map<std::string, const Json*>::const_iterator it =
        dictionaries_.find(key);
    return it != dictionaries_.end() ? it->second : NULL;
  }

  // Takes ownership of |dictionary|. Should be called only once per |key| and
  // per |dictionary|.
  void AddDictionary(const std::string& key, const Json* dictionary) {
    bool inserted =
        dictionaries_.insert(std::make_pair(key, dictionary)).second;
    // Cannot do work inside of assert(), because the compiler can optimize it
    // away.
    assert(inserted);
    // Avoid unused variable warning when assert() is optimized away.
    (void)inserted;
  }

  const std::vector<std::string>& GetKeys() const {
    return keys_;
  }

 private:
  void BuildKeyList() {
    assert(keys_.empty());
    for (Value::ConstMemberIterator it = value_->MemberBegin();
         it != value_->MemberEnd(); ++it) {
      keys_.push_back(it->name.GetString());
    }
  }

  // An owned JSON document. Can be NULL if the JSON document is not owned.
  //
  // When a JsonImpl object is constructed using a Document object, then
  // JsonImpl is supposed to take ownership of that object, making sure to
  // delete it in its own destructor. But when a JsonImpl object is constructed
  // using a Value object, then that object is owned by a Member object which is
  // owned by a Document object, and should therefore not be deleted by
  // JsonImpl.
  const scoped_ptr<const Document> document_;

  // A JSON document that is not owned. Cannot be NULL. Can point to document_.
  const Value* const value_;

  // Owned JSON objects.
  std::map<std::string, const Json*> dictionaries_;

  std::vector<std::string> keys_;

  DISALLOW_COPY_AND_ASSIGN(JsonImpl);
};

Json::Json() {}

Json::~Json() {}

bool Json::ParseObject(const std::string& json) {
  assert(impl_ == NULL);
  scoped_ptr<Document> document(new Document);
  document->Parse<kParseValidateEncodingFlag>(json.c_str());
  bool valid = !document->HasParseError() && document->IsObject();
  if (valid) {
    impl_.reset(new JsonImpl(document.release()));
  }
  return valid;
}

const std::vector<std::string>& Json::GetKeys() const {
  assert(impl_ != NULL);
  return impl_->GetKeys();
}

bool Json::GetStringValueForKey(const std::string& key,
                                std::string* value) const {
  assert(impl_ != NULL);
  assert(value != NULL);

  // Member is owned by impl_.
  const Value::Member* member = impl_->FindMember(key.c_str());
  if (member == NULL || !member->value.IsString()) {
    return false;
  }

  value->assign(member->value.GetString(), member->value.GetStringLength());
  return true;
}

bool Json::HasDictionaryValueForKey(const std::string& key) const {
  assert(impl_ != NULL);

  // The value returned by FindDictionary() is owned by impl_.
  if (impl_->FindDictionary(key) != NULL) {
    return true;
  }

  // Member is owned by impl_.
  const Value::Member* member = impl_->FindMember(key);
  return member != NULL && member->value.IsObject();
}

const Json& Json::GetDictionaryValueForKey(const std::string& key) const {
  assert(impl_ != NULL);

  // Existing_dictionary is owned by impl_.
  const Json* existing_dictionary = impl_->FindDictionary(key);
  if (existing_dictionary != NULL) {
    return *existing_dictionary;
  }

  // Member is owned by impl_.
  const Value::Member* member = impl_->FindMember(key);
  assert(member != NULL);
  assert(member->value.IsObject());

  // Dictionary is owned by impl_.
  Json* dictionary = new Json;
  dictionary->impl_.reset(new JsonImpl(&member->value));
  impl_->AddDictionary(key, dictionary);
  return *dictionary;
}

}  // namespace addressinput
}  // namespace i18n