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|. The key of |value| is app_id, and its value
42 // is URL to open the resource on the web app.
43 bool GetOpenWithLinksFromDictionaryValue(
44 const base::Value* value,
45 std::vector<FileResource::OpenWithLink>* result) {
49 const base::DictionaryValue* dictionary_value;
50 if (!value->GetAsDictionary(&dictionary_value))
53 result->reserve(dictionary_value->size());
54 for (DictionaryValue::Iterator iter(*dictionary_value);
55 !iter.IsAtEnd(); iter.Advance()) {
56 std::string string_value;
57 if (!iter.value().GetAsString(&string_value))
60 FileResource::OpenWithLink open_with_link;
61 open_with_link.app_id = iter.key();
62 open_with_link.open_url = GURL(string_value);
63 result->push_back(open_with_link);
69 // Drive v2 API JSON names.
71 // Definition order follows the order of documentation in
72 // https://developers.google.com/drive/v2/reference/
75 const char kKind[] = "kind";
76 const char kId[] = "id";
77 const char kETag[] = "etag";
78 const char kSelfLink[] = "selfLink";
79 const char kItems[] = "items";
80 const char kLargestChangeId[] = "largestChangeId";
83 // https://developers.google.com/drive/v2/reference/about
84 const char kAboutKind[] = "drive#about";
85 const char kQuotaBytesTotal[] = "quotaBytesTotal";
86 const char kQuotaBytesUsed[] = "quotaBytesUsed";
87 const char kRootFolderId[] = "rootFolderId";
90 // https://developers.google.com/drive/v2/reference/apps
91 const char kCategory[] = "category";
92 const char kSize[] = "size";
93 const char kIconUrl[] = "iconUrl";
96 // https://developers.google.com/drive/v2/reference/apps
97 const char kAppKind[] = "drive#app";
98 const char kName[] = "name";
99 const char kObjectType[] = "objectType";
100 const char kProductId[] = "productId";
101 const char kSupportsCreate[] = "supportsCreate";
102 const char kSupportsImport[] = "supportsImport";
103 const char kInstalled[] = "installed";
104 const char kAuthorized[] = "authorized";
105 const char kRemovable[] = "removable";
106 const char kPrimaryMimeTypes[] = "primaryMimeTypes";
107 const char kSecondaryMimeTypes[] = "secondaryMimeTypes";
108 const char kPrimaryFileExtensions[] = "primaryFileExtensions";
109 const char kSecondaryFileExtensions[] = "secondaryFileExtensions";
110 const char kIcons[] = "icons";
111 const char kCreateUrl[] = "createUrl";
114 // https://developers.google.com/drive/v2/reference/apps/list
115 const char kAppListKind[] = "drive#appList";
118 // https://developers.google.com/drive/v2/reference/parents
119 const char kParentReferenceKind[] = "drive#parentReference";
120 const char kParentLink[] = "parentLink";
121 const char kIsRoot[] = "isRoot";
124 // https://developers.google.com/drive/v2/reference/files
125 const char kFileKind[] = "drive#file";
126 const char kTitle[] = "title";
127 const char kMimeType[] = "mimeType";
128 const char kCreatedDate[] = "createdDate";
129 const char kModifiedDate[] = "modifiedDate";
130 const char kModifiedByMeDate[] = "modifiedByMeDate";
131 const char kLastViewedByMeDate[] = "lastViewedByMeDate";
132 const char kSharedWithMeDate[] = "sharedWithMeDate";
133 const char kDownloadUrl[] = "downloadUrl";
134 const char kFileExtension[] = "fileExtension";
135 const char kMd5Checksum[] = "md5Checksum";
136 const char kFileSize[] = "fileSize";
137 const char kAlternateLink[] = "alternateLink";
138 const char kEmbedLink[] = "embedLink";
139 const char kParents[] = "parents";
140 const char kThumbnailLink[] = "thumbnailLink";
141 const char kWebContentLink[] = "webContentLink";
142 const char kOpenWithLinks[] = "openWithLinks";
143 const char kLabels[] = "labels";
144 const char kImageMediaMetadata[] = "imageMediaMetadata";
145 const char kShared[] = "shared";
146 // These 5 flags are defined under |labels|.
147 const char kLabelStarred[] = "starred";
148 const char kLabelHidden[] = "hidden";
149 const char kLabelTrashed[] = "trashed";
150 const char kLabelRestricted[] = "restricted";
151 const char kLabelViewed[] = "viewed";
152 // These 3 flags are defined under |imageMediaMetadata|.
153 const char kImageMediaMetadataWidth[] = "width";
154 const char kImageMediaMetadataHeight[] = "height";
155 const char kImageMediaMetadataRotation[] = "rotation";
157 const char kDriveFolderMimeType[] = "application/vnd.google-apps.folder";
160 // https://developers.google.com/drive/v2/reference/files/list
161 const char kFileListKind[] = "drive#fileList";
162 const char kNextPageToken[] = "nextPageToken";
163 const char kNextLink[] = "nextLink";
166 // https://developers.google.com/drive/v2/reference/changes
167 const char kChangeKind[] = "drive#change";
168 const char kFileId[] = "fileId";
169 const char kDeleted[] = "deleted";
170 const char kFile[] = "file";
173 // https://developers.google.com/drive/v2/reference/changes/list
174 const char kChangeListKind[] = "drive#changeList";
176 // Maps category name to enum IconCategory.
177 struct AppIconCategoryMap {
178 DriveAppIcon::IconCategory category;
179 const char* category_name;
182 const AppIconCategoryMap kAppIconCategoryMap[] = {
183 { DriveAppIcon::DOCUMENT, "document" },
184 { DriveAppIcon::APPLICATION, "application" },
185 { DriveAppIcon::SHARED_DOCUMENT, "documentShared" },
188 // Checks if the JSON is expected kind. In Drive API, JSON data structure has
189 // |kind| property which denotes the type of the structure (e.g. "drive#file").
190 bool IsResourceKindExpected(const base::Value& value,
191 const std::string& expected_kind) {
192 const base::DictionaryValue* as_dict = NULL;
194 return value.GetAsDictionary(&as_dict) &&
195 as_dict->HasKey(kKind) &&
196 as_dict->GetString(kKind, &kind) &&
197 kind == expected_kind;
202 ////////////////////////////////////////////////////////////////////////////////
203 // AboutResource implementation
205 AboutResource::AboutResource()
206 : largest_change_id_(0),
207 quota_bytes_total_(0),
208 quota_bytes_used_(0) {}
210 AboutResource::~AboutResource() {}
213 scoped_ptr<AboutResource> AboutResource::CreateFrom(const base::Value& value) {
214 scoped_ptr<AboutResource> resource(new AboutResource());
215 if (!IsResourceKindExpected(value, kAboutKind) || !resource->Parse(value)) {
216 LOG(ERROR) << "Unable to create: Invalid About resource JSON!";
217 return scoped_ptr<AboutResource>();
219 return resource.Pass();
223 void AboutResource::RegisterJSONConverter(
224 base::JSONValueConverter<AboutResource>* converter) {
225 converter->RegisterCustomField<int64>(kLargestChangeId,
226 &AboutResource::largest_change_id_,
227 &base::StringToInt64);
228 converter->RegisterCustomField<int64>(kQuotaBytesTotal,
229 &AboutResource::quota_bytes_total_,
230 &base::StringToInt64);
231 converter->RegisterCustomField<int64>(kQuotaBytesUsed,
232 &AboutResource::quota_bytes_used_,
233 &base::StringToInt64);
234 converter->RegisterStringField(kRootFolderId,
235 &AboutResource::root_folder_id_);
238 bool AboutResource::Parse(const base::Value& value) {
239 base::JSONValueConverter<AboutResource> converter;
240 if (!converter.Convert(value, this)) {
241 LOG(ERROR) << "Unable to parse: Invalid About resource JSON!";
247 ////////////////////////////////////////////////////////////////////////////////
248 // DriveAppIcon implementation
250 DriveAppIcon::DriveAppIcon() : category_(UNKNOWN), icon_side_length_(0) {}
252 DriveAppIcon::~DriveAppIcon() {}
255 void DriveAppIcon::RegisterJSONConverter(
256 base::JSONValueConverter<DriveAppIcon>* converter) {
257 converter->RegisterCustomField<IconCategory>(
259 &DriveAppIcon::category_,
260 &DriveAppIcon::GetIconCategory);
261 converter->RegisterIntField(kSize, &DriveAppIcon::icon_side_length_);
262 converter->RegisterCustomField<GURL>(kIconUrl,
263 &DriveAppIcon::icon_url_,
268 scoped_ptr<DriveAppIcon> DriveAppIcon::CreateFrom(const base::Value& value) {
269 scoped_ptr<DriveAppIcon> resource(new DriveAppIcon());
270 if (!resource->Parse(value)) {
271 LOG(ERROR) << "Unable to create: Invalid DriveAppIcon JSON!";
272 return scoped_ptr<DriveAppIcon>();
274 return resource.Pass();
277 bool DriveAppIcon::Parse(const base::Value& value) {
278 base::JSONValueConverter<DriveAppIcon> converter;
279 if (!converter.Convert(value, this)) {
280 LOG(ERROR) << "Unable to parse: Invalid DriveAppIcon";
287 bool DriveAppIcon::GetIconCategory(const base::StringPiece& category,
288 DriveAppIcon::IconCategory* result) {
289 for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) {
290 if (category == kAppIconCategoryMap[i].category_name) {
291 *result = kAppIconCategoryMap[i].category;
295 DVLOG(1) << "Unknown icon category " << category;
299 ////////////////////////////////////////////////////////////////////////////////
300 // AppResource implementation
302 AppResource::AppResource()
303 : supports_create_(false),
304 supports_import_(false),
310 AppResource::~AppResource() {}
313 void AppResource::RegisterJSONConverter(
314 base::JSONValueConverter<AppResource>* converter) {
315 converter->RegisterStringField(kId, &AppResource::application_id_);
316 converter->RegisterStringField(kName, &AppResource::name_);
317 converter->RegisterStringField(kObjectType, &AppResource::object_type_);
318 converter->RegisterStringField(kProductId, &AppResource::product_id_);
319 converter->RegisterBoolField(kSupportsCreate, &AppResource::supports_create_);
320 converter->RegisterBoolField(kSupportsImport, &AppResource::supports_import_);
321 converter->RegisterBoolField(kInstalled, &AppResource::installed_);
322 converter->RegisterBoolField(kAuthorized, &AppResource::authorized_);
323 converter->RegisterBoolField(kRemovable, &AppResource::removable_);
324 converter->RegisterRepeatedString(kPrimaryMimeTypes,
325 &AppResource::primary_mimetypes_);
326 converter->RegisterRepeatedString(kSecondaryMimeTypes,
327 &AppResource::secondary_mimetypes_);
328 converter->RegisterRepeatedString(kPrimaryFileExtensions,
329 &AppResource::primary_file_extensions_);
330 converter->RegisterRepeatedString(kSecondaryFileExtensions,
331 &AppResource::secondary_file_extensions_);
332 converter->RegisterRepeatedMessage(kIcons, &AppResource::icons_);
333 converter->RegisterCustomField<GURL>(kCreateUrl,
334 &AppResource::create_url_,
339 scoped_ptr<AppResource> AppResource::CreateFrom(const base::Value& value) {
340 scoped_ptr<AppResource> resource(new AppResource());
341 if (!IsResourceKindExpected(value, kAppKind) || !resource->Parse(value)) {
342 LOG(ERROR) << "Unable to create: Invalid AppResource JSON!";
343 return scoped_ptr<AppResource>();
345 return resource.Pass();
348 bool AppResource::Parse(const base::Value& value) {
349 base::JSONValueConverter<AppResource> converter;
350 if (!converter.Convert(value, this)) {
351 LOG(ERROR) << "Unable to parse: Invalid AppResource";
357 ////////////////////////////////////////////////////////////////////////////////
358 // AppList implementation
360 AppList::AppList() {}
362 AppList::~AppList() {}
365 void AppList::RegisterJSONConverter(
366 base::JSONValueConverter<AppList>* converter) {
367 converter->RegisterStringField(kETag, &AppList::etag_);
368 converter->RegisterRepeatedMessage<AppResource>(kItems,
373 scoped_ptr<AppList> AppList::CreateFrom(const base::Value& value) {
374 scoped_ptr<AppList> resource(new AppList());
375 if (!IsResourceKindExpected(value, kAppListKind) || !resource->Parse(value)) {
376 LOG(ERROR) << "Unable to create: Invalid AppList JSON!";
377 return scoped_ptr<AppList>();
379 return resource.Pass();
382 bool AppList::Parse(const base::Value& value) {
383 base::JSONValueConverter<AppList> converter;
384 if (!converter.Convert(value, this)) {
385 LOG(ERROR) << "Unable to parse: Invalid AppList";
391 ////////////////////////////////////////////////////////////////////////////////
392 // ParentReference implementation
394 ParentReference::ParentReference() : is_root_(false) {}
396 ParentReference::~ParentReference() {}
399 void ParentReference::RegisterJSONConverter(
400 base::JSONValueConverter<ParentReference>* converter) {
401 converter->RegisterStringField(kId, &ParentReference::file_id_);
402 converter->RegisterCustomField<GURL>(kParentLink,
403 &ParentReference::parent_link_,
405 converter->RegisterBoolField(kIsRoot, &ParentReference::is_root_);
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->RegisterCustomField<GURL>(kSelfLink,
442 &FileResource::self_link_,
444 converter->RegisterStringField(kTitle, &FileResource::title_);
445 converter->RegisterStringField(kMimeType, &FileResource::mime_type_);
446 converter->RegisterNestedField(kLabels, &FileResource::labels_);
447 converter->RegisterNestedField(kImageMediaMetadata,
448 &FileResource::image_media_metadata_);
449 converter->RegisterCustomField<base::Time>(
451 &FileResource::created_date_,
452 &util::GetTimeFromString);
453 converter->RegisterCustomField<base::Time>(
455 &FileResource::modified_date_,
456 &util::GetTimeFromString);
457 converter->RegisterCustomField<base::Time>(
459 &FileResource::modified_by_me_date_,
460 &util::GetTimeFromString);
461 converter->RegisterCustomField<base::Time>(
463 &FileResource::last_viewed_by_me_date_,
464 &util::GetTimeFromString);
465 converter->RegisterCustomField<base::Time>(
467 &FileResource::shared_with_me_date_,
468 &util::GetTimeFromString);
469 converter->RegisterBoolField(kShared, &FileResource::shared_);
470 converter->RegisterCustomField<GURL>(kDownloadUrl,
471 &FileResource::download_url_,
473 converter->RegisterStringField(kFileExtension,
474 &FileResource::file_extension_);
475 converter->RegisterStringField(kMd5Checksum, &FileResource::md5_checksum_);
476 converter->RegisterCustomField<int64>(kFileSize,
477 &FileResource::file_size_,
478 &base::StringToInt64);
479 converter->RegisterCustomField<GURL>(kAlternateLink,
480 &FileResource::alternate_link_,
482 converter->RegisterCustomField<GURL>(kEmbedLink,
483 &FileResource::embed_link_,
485 converter->RegisterRepeatedMessage<ParentReference>(kParents,
486 &FileResource::parents_);
487 converter->RegisterCustomField<GURL>(kThumbnailLink,
488 &FileResource::thumbnail_link_,
490 converter->RegisterCustomField<GURL>(kWebContentLink,
491 &FileResource::web_content_link_,
493 converter->RegisterCustomValueField<std::vector<OpenWithLink> >(
495 &FileResource::open_with_links_,
496 GetOpenWithLinksFromDictionaryValue);
500 scoped_ptr<FileResource> FileResource::CreateFrom(const base::Value& value) {
501 scoped_ptr<FileResource> resource(new FileResource());
502 if (!IsResourceKindExpected(value, kFileKind) || !resource->Parse(value)) {
503 LOG(ERROR) << "Unable to create: Invalid FileResource JSON!";
504 return scoped_ptr<FileResource>();
506 return resource.Pass();
509 bool FileResource::IsDirectory() const {
510 return mime_type_ == kDriveFolderMimeType;
513 bool FileResource::Parse(const base::Value& value) {
514 base::JSONValueConverter<FileResource> converter;
515 if (!converter.Convert(value, this)) {
516 LOG(ERROR) << "Unable to parse: Invalid FileResource";
522 ////////////////////////////////////////////////////////////////////////////////
523 // FileList implementation
525 FileList::FileList() {}
527 FileList::~FileList() {}
530 void FileList::RegisterJSONConverter(
531 base::JSONValueConverter<FileList>* converter) {
532 converter->RegisterStringField(kETag, &FileList::etag_);
533 converter->RegisterStringField(kNextPageToken, &FileList::next_page_token_);
534 converter->RegisterCustomField<GURL>(kNextLink,
535 &FileList::next_link_,
537 converter->RegisterRepeatedMessage<FileResource>(kItems,
542 bool FileList::HasFileListKind(const base::Value& value) {
543 return IsResourceKindExpected(value, kFileListKind);
547 scoped_ptr<FileList> FileList::CreateFrom(const base::Value& value) {
548 scoped_ptr<FileList> resource(new FileList());
549 if (!HasFileListKind(value) || !resource->Parse(value)) {
550 LOG(ERROR) << "Unable to create: Invalid FileList JSON!";
551 return scoped_ptr<FileList>();
553 return resource.Pass();
556 bool FileList::Parse(const base::Value& value) {
557 base::JSONValueConverter<FileList> converter;
558 if (!converter.Convert(value, this)) {
559 LOG(ERROR) << "Unable to parse: Invalid FileList";
565 ////////////////////////////////////////////////////////////////////////////////
566 // ChangeResource implementation
568 ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {}
570 ChangeResource::~ChangeResource() {}
573 void ChangeResource::RegisterJSONConverter(
574 base::JSONValueConverter<ChangeResource>* converter) {
575 converter->RegisterCustomField<int64>(kId,
576 &ChangeResource::change_id_,
577 &base::StringToInt64);
578 converter->RegisterStringField(kFileId, &ChangeResource::file_id_);
579 converter->RegisterBoolField(kDeleted, &ChangeResource::deleted_);
580 converter->RegisterCustomValueField(kFile, &ChangeResource::file_,
581 &CreateFileResourceFromValue);
585 scoped_ptr<ChangeResource>
586 ChangeResource::CreateFrom(const base::Value& value) {
587 scoped_ptr<ChangeResource> resource(new ChangeResource());
588 if (!IsResourceKindExpected(value, kChangeKind) || !resource->Parse(value)) {
589 LOG(ERROR) << "Unable to create: Invalid ChangeResource JSON!";
590 return scoped_ptr<ChangeResource>();
592 return resource.Pass();
595 bool ChangeResource::Parse(const base::Value& value) {
596 base::JSONValueConverter<ChangeResource> converter;
597 if (!converter.Convert(value, this)) {
598 LOG(ERROR) << "Unable to parse: Invalid ChangeResource";
604 ////////////////////////////////////////////////////////////////////////////////
605 // ChangeList implementation
607 ChangeList::ChangeList() : largest_change_id_(0) {}
609 ChangeList::~ChangeList() {}
612 void ChangeList::RegisterJSONConverter(
613 base::JSONValueConverter<ChangeList>* converter) {
614 converter->RegisterStringField(kETag, &ChangeList::etag_);
615 converter->RegisterStringField(kNextPageToken, &ChangeList::next_page_token_);
616 converter->RegisterCustomField<GURL>(kNextLink,
617 &ChangeList::next_link_,
619 converter->RegisterCustomField<int64>(kLargestChangeId,
620 &ChangeList::largest_change_id_,
621 &base::StringToInt64);
622 converter->RegisterRepeatedMessage<ChangeResource>(kItems,
623 &ChangeList::items_);
627 bool ChangeList::HasChangeListKind(const base::Value& value) {
628 return IsResourceKindExpected(value, kChangeListKind);
632 scoped_ptr<ChangeList> ChangeList::CreateFrom(const base::Value& value) {
633 scoped_ptr<ChangeList> resource(new ChangeList());
634 if (!HasChangeListKind(value) || !resource->Parse(value)) {
635 LOG(ERROR) << "Unable to create: Invalid ChangeList JSON!";
636 return scoped_ptr<ChangeList>();
638 return resource.Pass();
641 bool ChangeList::Parse(const base::Value& value) {
642 base::JSONValueConverter<ChangeList> converter;
643 if (!converter.Convert(value, this)) {
644 LOG(ERROR) << "Unable to parse: Invalid ChangeList";
651 ////////////////////////////////////////////////////////////////////////////////
652 // FileLabels implementation
654 FileLabels::FileLabels()
661 FileLabels::~FileLabels() {}
664 void FileLabels::RegisterJSONConverter(
665 base::JSONValueConverter<FileLabels>* converter) {
666 converter->RegisterBoolField(kLabelStarred, &FileLabels::starred_);
667 converter->RegisterBoolField(kLabelHidden, &FileLabels::hidden_);
668 converter->RegisterBoolField(kLabelTrashed, &FileLabels::trashed_);
669 converter->RegisterBoolField(kLabelRestricted, &FileLabels::restricted_);
670 converter->RegisterBoolField(kLabelViewed, &FileLabels::viewed_);
674 scoped_ptr<FileLabels> FileLabels::CreateFrom(const base::Value& value) {
675 scoped_ptr<FileLabels> resource(new FileLabels());
676 if (!resource->Parse(value)) {
677 LOG(ERROR) << "Unable to create: Invalid FileLabels JSON!";
678 return scoped_ptr<FileLabels>();
680 return resource.Pass();
683 bool FileLabels::Parse(const base::Value& value) {
684 base::JSONValueConverter<FileLabels> converter;
685 if (!converter.Convert(value, this)) {
686 LOG(ERROR) << "Unable to parse: Invalid FileLabels.";
692 ////////////////////////////////////////////////////////////////////////////////
693 // ImageMediaMetadata implementation
695 ImageMediaMetadata::ImageMediaMetadata()
700 ImageMediaMetadata::~ImageMediaMetadata() {}
703 void ImageMediaMetadata::RegisterJSONConverter(
704 base::JSONValueConverter<ImageMediaMetadata>* converter) {
705 converter->RegisterIntField(kImageMediaMetadataWidth,
706 &ImageMediaMetadata::width_);
707 converter->RegisterIntField(kImageMediaMetadataHeight,
708 &ImageMediaMetadata::height_);
709 converter->RegisterIntField(kImageMediaMetadataRotation,
710 &ImageMediaMetadata::rotation_);
714 scoped_ptr<ImageMediaMetadata> ImageMediaMetadata::CreateFrom(
715 const base::Value& value) {
716 scoped_ptr<ImageMediaMetadata> resource(new ImageMediaMetadata());
717 if (!resource->Parse(value)) {
718 LOG(ERROR) << "Unable to create: Invalid ImageMediaMetadata JSON!";
719 return scoped_ptr<ImageMediaMetadata>();
721 return resource.Pass();
724 bool ImageMediaMetadata::Parse(const base::Value& value) {
726 base::JSONValueConverter<ImageMediaMetadata> converter;
727 if (!converter.Convert(value, this)) {
728 LOG(ERROR) << "Unable to parse: Invalid ImageMediaMetadata.";
734 } // namespace google_apis