1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "google_apis/drive/drive_api_parser.h"
9 #include "base/basictypes.h"
10 #include "base/files/file_path.h"
11 #include "base/json/json_value_converter.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/string_util.h"
16 #include "base/values.h"
17 #include "google_apis/drive/time_util.h"
20 using base::DictionaryValue
;
21 using base::ListValue
;
23 namespace google_apis
{
27 bool CreateFileResourceFromValue(const base::Value
* value
,
28 scoped_ptr
<FileResource
>* file
) {
29 *file
= FileResource::CreateFrom(*value
);
33 // Converts |url_string| to |result|. Always returns true to be used
34 // for JSONValueConverter::RegisterCustomField method.
35 // TODO(mukai): make it return false in case of invalid |url_string|.
36 bool GetGURLFromString(const base::StringPiece
& url_string
, GURL
* result
) {
37 *result
= GURL(url_string
.as_string());
41 // Converts |value| to |result|.
42 bool GetParentsFromValue(const base::Value
* value
,
43 std::vector
<ParentReference
>* result
) {
47 const base::ListValue
* list_value
= NULL
;
48 if (!value
->GetAsList(&list_value
))
51 base::JSONValueConverter
<ParentReference
> converter
;
52 result
->resize(list_value
->GetSize());
53 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
54 const base::Value
* parent_value
= NULL
;
55 if (!list_value
->Get(i
, &parent_value
) ||
56 !converter
.Convert(*parent_value
, &(*result
)[i
]))
63 // Converts |value| to |result|. The key of |value| is app_id, and its value
64 // is URL to open the resource on the web app.
65 bool GetOpenWithLinksFromDictionaryValue(
66 const base::Value
* value
,
67 std::vector
<FileResource::OpenWithLink
>* result
) {
71 const base::DictionaryValue
* dictionary_value
;
72 if (!value
->GetAsDictionary(&dictionary_value
))
75 result
->reserve(dictionary_value
->size());
76 for (DictionaryValue::Iterator
iter(*dictionary_value
);
77 !iter
.IsAtEnd(); iter
.Advance()) {
78 std::string string_value
;
79 if (!iter
.value().GetAsString(&string_value
))
82 FileResource::OpenWithLink open_with_link
;
83 open_with_link
.app_id
= iter
.key();
84 open_with_link
.open_url
= GURL(string_value
);
85 result
->push_back(open_with_link
);
91 // Drive v2 API JSON names.
93 // Definition order follows the order of documentation in
94 // https://developers.google.com/drive/v2/reference/
97 const char kKind
[] = "kind";
98 const char kId
[] = "id";
99 const char kETag
[] = "etag";
100 const char kItems
[] = "items";
101 const char kLargestChangeId
[] = "largestChangeId";
104 // https://developers.google.com/drive/v2/reference/about
105 const char kAboutKind
[] = "drive#about";
106 const char kQuotaBytesTotal
[] = "quotaBytesTotal";
107 const char kQuotaBytesUsed
[] = "quotaBytesUsed";
108 const char kRootFolderId
[] = "rootFolderId";
111 // https://developers.google.com/drive/v2/reference/apps
112 const char kCategory
[] = "category";
113 const char kSize
[] = "size";
114 const char kIconUrl
[] = "iconUrl";
117 // https://developers.google.com/drive/v2/reference/apps
118 const char kAppKind
[] = "drive#app";
119 const char kName
[] = "name";
120 const char kObjectType
[] = "objectType";
121 const char kProductId
[] = "productId";
122 const char kSupportsCreate
[] = "supportsCreate";
123 const char kRemovable
[] = "removable";
124 const char kPrimaryMimeTypes
[] = "primaryMimeTypes";
125 const char kSecondaryMimeTypes
[] = "secondaryMimeTypes";
126 const char kPrimaryFileExtensions
[] = "primaryFileExtensions";
127 const char kSecondaryFileExtensions
[] = "secondaryFileExtensions";
128 const char kIcons
[] = "icons";
129 const char kCreateUrl
[] = "createUrl";
132 // https://developers.google.com/drive/v2/reference/apps/list
133 const char kAppListKind
[] = "drive#appList";
136 // https://developers.google.com/drive/v2/reference/parents
137 const char kParentReferenceKind
[] = "drive#parentReference";
138 const char kParentLink
[] = "parentLink";
141 // https://developers.google.com/drive/v2/reference/files
142 const char kFileKind
[] = "drive#file";
143 const char kTitle
[] = "title";
144 const char kMimeType
[] = "mimeType";
145 const char kCreatedDate
[] = "createdDate";
146 const char kModificationDate
[] = "modificationDate";
147 const char kModifiedDate
[] = "modifiedDate";
148 const char kLastViewedByMeDate
[] = "lastViewedByMeDate";
149 const char kSharedWithMeDate
[] = "sharedWithMeDate";
150 const char kMd5Checksum
[] = "md5Checksum";
151 const char kFileSize
[] = "fileSize";
152 const char kAlternateLink
[] = "alternateLink";
153 const char kParents
[] = "parents";
154 const char kOpenWithLinks
[] = "openWithLinks";
155 const char kLabels
[] = "labels";
156 const char kImageMediaMetadata
[] = "imageMediaMetadata";
157 const char kShared
[] = "shared";
158 // These 5 flags are defined under |labels|.
159 const char kLabelTrashed
[] = "trashed";
160 // These 3 flags are defined under |imageMediaMetadata|.
161 const char kImageMediaMetadataWidth
[] = "width";
162 const char kImageMediaMetadataHeight
[] = "height";
163 const char kImageMediaMetadataRotation
[] = "rotation";
165 const char kDriveFolderMimeType
[] = "application/vnd.google-apps.folder";
168 // https://developers.google.com/drive/v2/reference/files/list
169 const char kFileListKind
[] = "drive#fileList";
170 const char kNextLink
[] = "nextLink";
173 // https://developers.google.com/drive/v2/reference/changes
174 const char kChangeKind
[] = "drive#change";
175 const char kFileId
[] = "fileId";
176 const char kDeleted
[] = "deleted";
177 const char kFile
[] = "file";
180 // https://developers.google.com/drive/v2/reference/changes/list
181 const char kChangeListKind
[] = "drive#changeList";
183 // Maps category name to enum IconCategory.
184 struct AppIconCategoryMap
{
185 DriveAppIcon::IconCategory category
;
186 const char* category_name
;
189 const AppIconCategoryMap kAppIconCategoryMap
[] = {
190 { DriveAppIcon::DOCUMENT
, "document" },
191 { DriveAppIcon::APPLICATION
, "application" },
192 { DriveAppIcon::SHARED_DOCUMENT
, "documentShared" },
195 // Checks if the JSON is expected kind. In Drive API, JSON data structure has
196 // |kind| property which denotes the type of the structure (e.g. "drive#file").
197 bool IsResourceKindExpected(const base::Value
& value
,
198 const std::string
& expected_kind
) {
199 const base::DictionaryValue
* as_dict
= NULL
;
201 return value
.GetAsDictionary(&as_dict
) &&
202 as_dict
->HasKey(kKind
) &&
203 as_dict
->GetString(kKind
, &kind
) &&
204 kind
== expected_kind
;
209 ////////////////////////////////////////////////////////////////////////////////
210 // AboutResource implementation
212 AboutResource::AboutResource()
213 : largest_change_id_(0),
214 quota_bytes_total_(0),
215 quota_bytes_used_(0) {}
217 AboutResource::~AboutResource() {}
220 scoped_ptr
<AboutResource
> AboutResource::CreateFrom(const base::Value
& value
) {
221 scoped_ptr
<AboutResource
> resource(new AboutResource());
222 if (!IsResourceKindExpected(value
, kAboutKind
) || !resource
->Parse(value
)) {
223 LOG(ERROR
) << "Unable to create: Invalid About resource JSON!";
224 return scoped_ptr
<AboutResource
>();
226 return resource
.Pass();
230 void AboutResource::RegisterJSONConverter(
231 base::JSONValueConverter
<AboutResource
>* converter
) {
232 converter
->RegisterCustomField
<int64
>(kLargestChangeId
,
233 &AboutResource::largest_change_id_
,
234 &base::StringToInt64
);
235 converter
->RegisterCustomField
<int64
>(kQuotaBytesTotal
,
236 &AboutResource::quota_bytes_total_
,
237 &base::StringToInt64
);
238 converter
->RegisterCustomField
<int64
>(kQuotaBytesUsed
,
239 &AboutResource::quota_bytes_used_
,
240 &base::StringToInt64
);
241 converter
->RegisterStringField(kRootFolderId
,
242 &AboutResource::root_folder_id_
);
245 bool AboutResource::Parse(const base::Value
& value
) {
246 base::JSONValueConverter
<AboutResource
> converter
;
247 if (!converter
.Convert(value
, this)) {
248 LOG(ERROR
) << "Unable to parse: Invalid About resource JSON!";
254 ////////////////////////////////////////////////////////////////////////////////
255 // DriveAppIcon implementation
257 DriveAppIcon::DriveAppIcon() : category_(UNKNOWN
), icon_side_length_(0) {}
259 DriveAppIcon::~DriveAppIcon() {}
262 void DriveAppIcon::RegisterJSONConverter(
263 base::JSONValueConverter
<DriveAppIcon
>* converter
) {
264 converter
->RegisterCustomField
<IconCategory
>(
266 &DriveAppIcon::category_
,
267 &DriveAppIcon::GetIconCategory
);
268 converter
->RegisterIntField(kSize
, &DriveAppIcon::icon_side_length_
);
269 converter
->RegisterCustomField
<GURL
>(kIconUrl
,
270 &DriveAppIcon::icon_url_
,
275 scoped_ptr
<DriveAppIcon
> DriveAppIcon::CreateFrom(const base::Value
& value
) {
276 scoped_ptr
<DriveAppIcon
> resource(new DriveAppIcon());
277 if (!resource
->Parse(value
)) {
278 LOG(ERROR
) << "Unable to create: Invalid DriveAppIcon JSON!";
279 return scoped_ptr
<DriveAppIcon
>();
281 return resource
.Pass();
284 bool DriveAppIcon::Parse(const base::Value
& value
) {
285 base::JSONValueConverter
<DriveAppIcon
> converter
;
286 if (!converter
.Convert(value
, this)) {
287 LOG(ERROR
) << "Unable to parse: Invalid DriveAppIcon";
294 bool DriveAppIcon::GetIconCategory(const base::StringPiece
& category
,
295 DriveAppIcon::IconCategory
* result
) {
296 for (size_t i
= 0; i
< arraysize(kAppIconCategoryMap
); i
++) {
297 if (category
== kAppIconCategoryMap
[i
].category_name
) {
298 *result
= kAppIconCategoryMap
[i
].category
;
302 DVLOG(1) << "Unknown icon category " << category
;
306 ////////////////////////////////////////////////////////////////////////////////
307 // AppResource implementation
309 AppResource::AppResource()
310 : supports_create_(false),
314 AppResource::~AppResource() {}
317 void AppResource::RegisterJSONConverter(
318 base::JSONValueConverter
<AppResource
>* converter
) {
319 converter
->RegisterStringField(kId
, &AppResource::application_id_
);
320 converter
->RegisterStringField(kName
, &AppResource::name_
);
321 converter
->RegisterStringField(kObjectType
, &AppResource::object_type_
);
322 converter
->RegisterStringField(kProductId
, &AppResource::product_id_
);
323 converter
->RegisterBoolField(kSupportsCreate
, &AppResource::supports_create_
);
324 converter
->RegisterBoolField(kRemovable
, &AppResource::removable_
);
325 converter
->RegisterRepeatedString(kPrimaryMimeTypes
,
326 &AppResource::primary_mimetypes_
);
327 converter
->RegisterRepeatedString(kSecondaryMimeTypes
,
328 &AppResource::secondary_mimetypes_
);
329 converter
->RegisterRepeatedString(kPrimaryFileExtensions
,
330 &AppResource::primary_file_extensions_
);
331 converter
->RegisterRepeatedString(kSecondaryFileExtensions
,
332 &AppResource::secondary_file_extensions_
);
333 converter
->RegisterRepeatedMessage(kIcons
, &AppResource::icons_
);
334 converter
->RegisterCustomField
<GURL
>(kCreateUrl
,
335 &AppResource::create_url_
,
340 scoped_ptr
<AppResource
> AppResource::CreateFrom(const base::Value
& value
) {
341 scoped_ptr
<AppResource
> resource(new AppResource());
342 if (!IsResourceKindExpected(value
, kAppKind
) || !resource
->Parse(value
)) {
343 LOG(ERROR
) << "Unable to create: Invalid AppResource JSON!";
344 return scoped_ptr
<AppResource
>();
346 return resource
.Pass();
349 bool AppResource::Parse(const base::Value
& value
) {
350 base::JSONValueConverter
<AppResource
> converter
;
351 if (!converter
.Convert(value
, this)) {
352 LOG(ERROR
) << "Unable to parse: Invalid AppResource";
358 ////////////////////////////////////////////////////////////////////////////////
359 // AppList implementation
361 AppList::AppList() {}
363 AppList::~AppList() {}
366 void AppList::RegisterJSONConverter(
367 base::JSONValueConverter
<AppList
>* converter
) {
368 converter
->RegisterStringField(kETag
, &AppList::etag_
);
369 converter
->RegisterRepeatedMessage
<AppResource
>(kItems
,
374 scoped_ptr
<AppList
> AppList::CreateFrom(const base::Value
& value
) {
375 scoped_ptr
<AppList
> resource(new AppList());
376 if (!IsResourceKindExpected(value
, kAppListKind
) || !resource
->Parse(value
)) {
377 LOG(ERROR
) << "Unable to create: Invalid AppList JSON!";
378 return scoped_ptr
<AppList
>();
380 return resource
.Pass();
383 bool AppList::Parse(const base::Value
& value
) {
384 base::JSONValueConverter
<AppList
> converter
;
385 if (!converter
.Convert(value
, this)) {
386 LOG(ERROR
) << "Unable to parse: Invalid AppList";
392 ////////////////////////////////////////////////////////////////////////////////
393 // ParentReference implementation
395 ParentReference::ParentReference() {}
397 ParentReference::~ParentReference() {}
400 void ParentReference::RegisterJSONConverter(
401 base::JSONValueConverter
<ParentReference
>* converter
) {
402 converter
->RegisterStringField(kId
, &ParentReference::file_id_
);
403 converter
->RegisterCustomField
<GURL
>(kParentLink
,
404 &ParentReference::parent_link_
,
409 scoped_ptr
<ParentReference
>
410 ParentReference::CreateFrom(const base::Value
& value
) {
411 scoped_ptr
<ParentReference
> reference(new ParentReference());
412 if (!IsResourceKindExpected(value
, kParentReferenceKind
) ||
413 !reference
->Parse(value
)) {
414 LOG(ERROR
) << "Unable to create: Invalid ParentRefernce JSON!";
415 return scoped_ptr
<ParentReference
>();
417 return reference
.Pass();
420 bool ParentReference::Parse(const base::Value
& value
) {
421 base::JSONValueConverter
<ParentReference
> converter
;
422 if (!converter
.Convert(value
, this)) {
423 LOG(ERROR
) << "Unable to parse: Invalid ParentReference";
429 ////////////////////////////////////////////////////////////////////////////////
430 // FileResource implementation
432 FileResource::FileResource() : shared_(false), file_size_(0) {}
434 FileResource::~FileResource() {}
437 void FileResource::RegisterJSONConverter(
438 base::JSONValueConverter
<FileResource
>* converter
) {
439 converter
->RegisterStringField(kId
, &FileResource::file_id_
);
440 converter
->RegisterStringField(kETag
, &FileResource::etag_
);
441 converter
->RegisterStringField(kTitle
, &FileResource::title_
);
442 converter
->RegisterStringField(kMimeType
, &FileResource::mime_type_
);
443 converter
->RegisterNestedField(kLabels
, &FileResource::labels_
);
444 converter
->RegisterNestedField(kImageMediaMetadata
,
445 &FileResource::image_media_metadata_
);
446 converter
->RegisterCustomField
<base::Time
>(
448 &FileResource::created_date_
,
449 &util::GetTimeFromString
);
450 converter
->RegisterCustomField
<base::Time
>(
452 &FileResource::modified_date_
,
453 &util::GetTimeFromString
);
454 converter
->RegisterCustomField
<base::Time
>(
456 &FileResource::last_viewed_by_me_date_
,
457 &util::GetTimeFromString
);
458 converter
->RegisterCustomField
<base::Time
>(
460 &FileResource::shared_with_me_date_
,
461 &util::GetTimeFromString
);
462 converter
->RegisterBoolField(kShared
, &FileResource::shared_
);
463 converter
->RegisterStringField(kMd5Checksum
, &FileResource::md5_checksum_
);
464 converter
->RegisterCustomField
<int64
>(kFileSize
,
465 &FileResource::file_size_
,
466 &base::StringToInt64
);
467 converter
->RegisterCustomField
<GURL
>(kAlternateLink
,
468 &FileResource::alternate_link_
,
470 converter
->RegisterCustomValueField
<std::vector
<ParentReference
> >(
472 &FileResource::parents_
,
473 GetParentsFromValue
);
474 converter
->RegisterCustomValueField
<std::vector
<OpenWithLink
> >(
476 &FileResource::open_with_links_
,
477 GetOpenWithLinksFromDictionaryValue
);
481 scoped_ptr
<FileResource
> FileResource::CreateFrom(const base::Value
& value
) {
482 scoped_ptr
<FileResource
> resource(new FileResource());
483 if (!IsResourceKindExpected(value
, kFileKind
) || !resource
->Parse(value
)) {
484 LOG(ERROR
) << "Unable to create: Invalid FileResource JSON!";
485 return scoped_ptr
<FileResource
>();
487 return resource
.Pass();
490 bool FileResource::IsDirectory() const {
491 return mime_type_
== kDriveFolderMimeType
;
494 bool FileResource::Parse(const base::Value
& value
) {
495 base::JSONValueConverter
<FileResource
> converter
;
496 if (!converter
.Convert(value
, this)) {
497 LOG(ERROR
) << "Unable to parse: Invalid FileResource";
503 ////////////////////////////////////////////////////////////////////////////////
504 // FileList implementation
506 FileList::FileList() {}
508 FileList::~FileList() {}
511 void FileList::RegisterJSONConverter(
512 base::JSONValueConverter
<FileList
>* converter
) {
513 converter
->RegisterCustomField
<GURL
>(kNextLink
,
514 &FileList::next_link_
,
516 converter
->RegisterRepeatedMessage
<FileResource
>(kItems
,
521 bool FileList::HasFileListKind(const base::Value
& value
) {
522 return IsResourceKindExpected(value
, kFileListKind
);
526 scoped_ptr
<FileList
> FileList::CreateFrom(const base::Value
& value
) {
527 scoped_ptr
<FileList
> resource(new FileList());
528 if (!HasFileListKind(value
) || !resource
->Parse(value
)) {
529 LOG(ERROR
) << "Unable to create: Invalid FileList JSON!";
530 return scoped_ptr
<FileList
>();
532 return resource
.Pass();
535 bool FileList::Parse(const base::Value
& value
) {
536 base::JSONValueConverter
<FileList
> converter
;
537 if (!converter
.Convert(value
, this)) {
538 LOG(ERROR
) << "Unable to parse: Invalid FileList";
544 ////////////////////////////////////////////////////////////////////////////////
545 // ChangeResource implementation
547 ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {}
549 ChangeResource::~ChangeResource() {}
552 void ChangeResource::RegisterJSONConverter(
553 base::JSONValueConverter
<ChangeResource
>* converter
) {
554 converter
->RegisterCustomField
<int64
>(kId
,
555 &ChangeResource::change_id_
,
556 &base::StringToInt64
);
557 converter
->RegisterStringField(kFileId
, &ChangeResource::file_id_
);
558 converter
->RegisterBoolField(kDeleted
, &ChangeResource::deleted_
);
559 converter
->RegisterCustomValueField(kFile
, &ChangeResource::file_
,
560 &CreateFileResourceFromValue
);
561 converter
->RegisterCustomField
<base::Time
>(
562 kModificationDate
, &ChangeResource::modification_date_
,
563 &util::GetTimeFromString
);
567 scoped_ptr
<ChangeResource
>
568 ChangeResource::CreateFrom(const base::Value
& value
) {
569 scoped_ptr
<ChangeResource
> resource(new ChangeResource());
570 if (!IsResourceKindExpected(value
, kChangeKind
) || !resource
->Parse(value
)) {
571 LOG(ERROR
) << "Unable to create: Invalid ChangeResource JSON!";
572 return scoped_ptr
<ChangeResource
>();
574 return resource
.Pass();
577 bool ChangeResource::Parse(const base::Value
& value
) {
578 base::JSONValueConverter
<ChangeResource
> converter
;
579 if (!converter
.Convert(value
, this)) {
580 LOG(ERROR
) << "Unable to parse: Invalid ChangeResource";
586 ////////////////////////////////////////////////////////////////////////////////
587 // ChangeList implementation
589 ChangeList::ChangeList() : largest_change_id_(0) {}
591 ChangeList::~ChangeList() {}
594 void ChangeList::RegisterJSONConverter(
595 base::JSONValueConverter
<ChangeList
>* converter
) {
596 converter
->RegisterCustomField
<GURL
>(kNextLink
,
597 &ChangeList::next_link_
,
599 converter
->RegisterCustomField
<int64
>(kLargestChangeId
,
600 &ChangeList::largest_change_id_
,
601 &base::StringToInt64
);
602 converter
->RegisterRepeatedMessage
<ChangeResource
>(kItems
,
603 &ChangeList::items_
);
607 bool ChangeList::HasChangeListKind(const base::Value
& value
) {
608 return IsResourceKindExpected(value
, kChangeListKind
);
612 scoped_ptr
<ChangeList
> ChangeList::CreateFrom(const base::Value
& value
) {
613 scoped_ptr
<ChangeList
> resource(new ChangeList());
614 if (!HasChangeListKind(value
) || !resource
->Parse(value
)) {
615 LOG(ERROR
) << "Unable to create: Invalid ChangeList JSON!";
616 return scoped_ptr
<ChangeList
>();
618 return resource
.Pass();
621 bool ChangeList::Parse(const base::Value
& value
) {
622 base::JSONValueConverter
<ChangeList
> converter
;
623 if (!converter
.Convert(value
, this)) {
624 LOG(ERROR
) << "Unable to parse: Invalid ChangeList";
631 ////////////////////////////////////////////////////////////////////////////////
632 // FileLabels implementation
634 FileLabels::FileLabels() : trashed_(false) {}
636 FileLabels::~FileLabels() {}
639 void FileLabels::RegisterJSONConverter(
640 base::JSONValueConverter
<FileLabels
>* converter
) {
641 converter
->RegisterBoolField(kLabelTrashed
, &FileLabels::trashed_
);
645 scoped_ptr
<FileLabels
> FileLabels::CreateFrom(const base::Value
& value
) {
646 scoped_ptr
<FileLabels
> resource(new FileLabels());
647 if (!resource
->Parse(value
)) {
648 LOG(ERROR
) << "Unable to create: Invalid FileLabels JSON!";
649 return scoped_ptr
<FileLabels
>();
651 return resource
.Pass();
654 bool FileLabels::Parse(const base::Value
& value
) {
655 base::JSONValueConverter
<FileLabels
> converter
;
656 if (!converter
.Convert(value
, this)) {
657 LOG(ERROR
) << "Unable to parse: Invalid FileLabels.";
663 ////////////////////////////////////////////////////////////////////////////////
664 // ImageMediaMetadata implementation
666 ImageMediaMetadata::ImageMediaMetadata()
671 ImageMediaMetadata::~ImageMediaMetadata() {}
674 void ImageMediaMetadata::RegisterJSONConverter(
675 base::JSONValueConverter
<ImageMediaMetadata
>* converter
) {
676 converter
->RegisterIntField(kImageMediaMetadataWidth
,
677 &ImageMediaMetadata::width_
);
678 converter
->RegisterIntField(kImageMediaMetadataHeight
,
679 &ImageMediaMetadata::height_
);
680 converter
->RegisterIntField(kImageMediaMetadataRotation
,
681 &ImageMediaMetadata::rotation_
);
685 scoped_ptr
<ImageMediaMetadata
> ImageMediaMetadata::CreateFrom(
686 const base::Value
& value
) {
687 scoped_ptr
<ImageMediaMetadata
> resource(new ImageMediaMetadata());
688 if (!resource
->Parse(value
)) {
689 LOG(ERROR
) << "Unable to create: Invalid ImageMediaMetadata JSON!";
690 return scoped_ptr
<ImageMediaMetadata
>();
692 return resource
.Pass();
695 bool ImageMediaMetadata::Parse(const base::Value
& value
) {
696 base::JSONValueConverter
<ImageMediaMetadata
> converter
;
697 if (!converter
.Convert(value
, this)) {
698 LOG(ERROR
) << "Unable to parse: Invalid ImageMediaMetadata.";
704 } // namespace google_apis