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"
7 #include "base/basictypes.h"
8 #include "base/json/json_value_converter.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "google_apis/drive/time_util.h"
16 namespace google_apis
{
20 const int64 kUnsetFileSize
= -1;
22 bool CreateFileResourceFromValue(const base::Value
* value
,
23 scoped_ptr
<FileResource
>* file
) {
24 *file
= FileResource::CreateFrom(*value
);
28 // Converts |url_string| to |result|. Always returns true to be used
29 // for JSONValueConverter::RegisterCustomField method.
30 // TODO(mukai): make it return false in case of invalid |url_string|.
31 bool GetGURLFromString(const base::StringPiece
& url_string
, GURL
* result
) {
32 *result
= GURL(url_string
.as_string());
36 // Converts |value| to |result|.
37 bool GetParentsFromValue(const base::Value
* value
,
38 std::vector
<ParentReference
>* result
) {
42 const base::ListValue
* list_value
= NULL
;
43 if (!value
->GetAsList(&list_value
))
46 base::JSONValueConverter
<ParentReference
> converter
;
47 result
->resize(list_value
->GetSize());
48 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
49 const base::Value
* parent_value
= NULL
;
50 if (!list_value
->Get(i
, &parent_value
) ||
51 !converter
.Convert(*parent_value
, &(*result
)[i
]))
58 // Converts |value| to |result|. The key of |value| is app_id, and its value
59 // is URL to open the resource on the web app.
60 bool GetOpenWithLinksFromDictionaryValue(
61 const base::Value
* value
,
62 std::vector
<FileResource::OpenWithLink
>* result
) {
66 const base::DictionaryValue
* dictionary_value
;
67 if (!value
->GetAsDictionary(&dictionary_value
))
70 result
->reserve(dictionary_value
->size());
71 for (base::DictionaryValue::Iterator
iter(*dictionary_value
);
72 !iter
.IsAtEnd(); iter
.Advance()) {
73 std::string string_value
;
74 if (!iter
.value().GetAsString(&string_value
))
77 FileResource::OpenWithLink open_with_link
;
78 open_with_link
.app_id
= iter
.key();
79 open_with_link
.open_url
= GURL(string_value
);
80 result
->push_back(open_with_link
);
86 // Drive v2 API JSON names.
88 // Definition order follows the order of documentation in
89 // https://developers.google.com/drive/v2/reference/
92 const char kKind
[] = "kind";
93 const char kId
[] = "id";
94 const char kETag
[] = "etag";
95 const char kItems
[] = "items";
96 const char kLargestChangeId
[] = "largestChangeId";
99 // https://developers.google.com/drive/v2/reference/about
100 const char kAboutKind
[] = "drive#about";
101 const char kQuotaBytesTotal
[] = "quotaBytesTotal";
102 const char kQuotaBytesUsed
[] = "quotaBytesUsed";
103 const char kRootFolderId
[] = "rootFolderId";
106 // https://developers.google.com/drive/v2/reference/apps
107 const char kCategory
[] = "category";
108 const char kSize
[] = "size";
109 const char kIconUrl
[] = "iconUrl";
112 // https://developers.google.com/drive/v2/reference/apps
113 const char kAppKind
[] = "drive#app";
114 const char kName
[] = "name";
115 const char kObjectType
[] = "objectType";
116 const char kProductId
[] = "productId";
117 const char kSupportsCreate
[] = "supportsCreate";
118 const char kRemovable
[] = "removable";
119 const char kPrimaryMimeTypes
[] = "primaryMimeTypes";
120 const char kSecondaryMimeTypes
[] = "secondaryMimeTypes";
121 const char kPrimaryFileExtensions
[] = "primaryFileExtensions";
122 const char kSecondaryFileExtensions
[] = "secondaryFileExtensions";
123 const char kIcons
[] = "icons";
124 const char kCreateUrl
[] = "createUrl";
127 // https://developers.google.com/drive/v2/reference/apps/list
128 const char kAppListKind
[] = "drive#appList";
131 // https://developers.google.com/drive/v2/reference/parents
132 const char kParentReferenceKind
[] = "drive#parentReference";
133 const char kParentLink
[] = "parentLink";
136 // https://developers.google.com/drive/v2/reference/files
137 const char kFileKind
[] = "drive#file";
138 const char kTitle
[] = "title";
139 const char kMimeType
[] = "mimeType";
140 const char kCreatedDate
[] = "createdDate";
141 const char kModificationDate
[] = "modificationDate";
142 const char kModifiedDate
[] = "modifiedDate";
143 const char kLastViewedByMeDate
[] = "lastViewedByMeDate";
144 const char kSharedWithMeDate
[] = "sharedWithMeDate";
145 const char kMd5Checksum
[] = "md5Checksum";
146 const char kFileSize
[] = "fileSize";
147 const char kAlternateLink
[] = "alternateLink";
148 const char kParents
[] = "parents";
149 const char kOpenWithLinks
[] = "openWithLinks";
150 const char kLabels
[] = "labels";
151 const char kImageMediaMetadata
[] = "imageMediaMetadata";
152 const char kShared
[] = "shared";
153 // These 5 flags are defined under |labels|.
154 const char kLabelTrashed
[] = "trashed";
155 // These 3 flags are defined under |imageMediaMetadata|.
156 const char kImageMediaMetadataWidth
[] = "width";
157 const char kImageMediaMetadataHeight
[] = "height";
158 const char kImageMediaMetadataRotation
[] = "rotation";
160 const char kDriveFolderMimeType
[] = "application/vnd.google-apps.folder";
163 // https://developers.google.com/drive/v2/reference/files/list
164 const char kFileListKind
[] = "drive#fileList";
165 const char kNextLink
[] = "nextLink";
168 // https://developers.google.com/drive/v2/reference/changes
169 const char kChangeKind
[] = "drive#change";
170 const char kFileId
[] = "fileId";
171 const char kDeleted
[] = "deleted";
172 const char kFile
[] = "file";
175 // https://developers.google.com/drive/v2/reference/changes/list
176 const char kChangeListKind
[] = "drive#changeList";
178 // Maps category name to enum IconCategory.
179 struct AppIconCategoryMap
{
180 DriveAppIcon::IconCategory category
;
181 const char* category_name
;
184 const AppIconCategoryMap kAppIconCategoryMap
[] = {
185 { DriveAppIcon::DOCUMENT
, "document" },
186 { DriveAppIcon::APPLICATION
, "application" },
187 { DriveAppIcon::SHARED_DOCUMENT
, "documentShared" },
190 // Checks if the JSON is expected kind. In Drive API, JSON data structure has
191 // |kind| property which denotes the type of the structure (e.g. "drive#file").
192 bool IsResourceKindExpected(const base::Value
& value
,
193 const std::string
& expected_kind
) {
194 const base::DictionaryValue
* as_dict
= NULL
;
196 return value
.GetAsDictionary(&as_dict
) &&
197 as_dict
->HasKey(kKind
) &&
198 as_dict
->GetString(kKind
, &kind
) &&
199 kind
== expected_kind
;
204 ////////////////////////////////////////////////////////////////////////////////
205 // AboutResource implementation
207 AboutResource::AboutResource()
208 : largest_change_id_(0),
209 quota_bytes_total_(0),
210 quota_bytes_used_(0) {}
212 AboutResource::~AboutResource() {}
215 scoped_ptr
<AboutResource
> AboutResource::CreateFrom(const base::Value
& value
) {
216 scoped_ptr
<AboutResource
> resource(new AboutResource());
217 if (!IsResourceKindExpected(value
, kAboutKind
) || !resource
->Parse(value
)) {
218 LOG(ERROR
) << "Unable to create: Invalid About resource JSON!";
219 return scoped_ptr
<AboutResource
>();
221 return resource
.Pass();
225 void AboutResource::RegisterJSONConverter(
226 base::JSONValueConverter
<AboutResource
>* converter
) {
227 converter
->RegisterCustomField
<int64
>(kLargestChangeId
,
228 &AboutResource::largest_change_id_
,
229 &base::StringToInt64
);
230 converter
->RegisterCustomField
<int64
>(kQuotaBytesTotal
,
231 &AboutResource::quota_bytes_total_
,
232 &base::StringToInt64
);
233 converter
->RegisterCustomField
<int64
>(kQuotaBytesUsed
,
234 &AboutResource::quota_bytes_used_
,
235 &base::StringToInt64
);
236 converter
->RegisterStringField(kRootFolderId
,
237 &AboutResource::root_folder_id_
);
240 bool AboutResource::Parse(const base::Value
& value
) {
241 base::JSONValueConverter
<AboutResource
> converter
;
242 if (!converter
.Convert(value
, this)) {
243 LOG(ERROR
) << "Unable to parse: Invalid About resource JSON!";
249 ////////////////////////////////////////////////////////////////////////////////
250 // DriveAppIcon implementation
252 DriveAppIcon::DriveAppIcon() : category_(UNKNOWN
), icon_side_length_(0) {}
254 DriveAppIcon::~DriveAppIcon() {}
257 void DriveAppIcon::RegisterJSONConverter(
258 base::JSONValueConverter
<DriveAppIcon
>* converter
) {
259 converter
->RegisterCustomField
<IconCategory
>(
261 &DriveAppIcon::category_
,
262 &DriveAppIcon::GetIconCategory
);
263 converter
->RegisterIntField(kSize
, &DriveAppIcon::icon_side_length_
);
264 converter
->RegisterCustomField
<GURL
>(kIconUrl
,
265 &DriveAppIcon::icon_url_
,
270 scoped_ptr
<DriveAppIcon
> DriveAppIcon::CreateFrom(const base::Value
& value
) {
271 scoped_ptr
<DriveAppIcon
> resource(new DriveAppIcon());
272 if (!resource
->Parse(value
)) {
273 LOG(ERROR
) << "Unable to create: Invalid DriveAppIcon JSON!";
274 return scoped_ptr
<DriveAppIcon
>();
276 return resource
.Pass();
279 bool DriveAppIcon::Parse(const base::Value
& value
) {
280 base::JSONValueConverter
<DriveAppIcon
> converter
;
281 if (!converter
.Convert(value
, this)) {
282 LOG(ERROR
) << "Unable to parse: Invalid DriveAppIcon";
289 bool DriveAppIcon::GetIconCategory(const base::StringPiece
& category
,
290 DriveAppIcon::IconCategory
* result
) {
291 for (size_t i
= 0; i
< arraysize(kAppIconCategoryMap
); i
++) {
292 if (category
== kAppIconCategoryMap
[i
].category_name
) {
293 *result
= kAppIconCategoryMap
[i
].category
;
297 DVLOG(1) << "Unknown icon category " << category
;
301 ////////////////////////////////////////////////////////////////////////////////
302 // AppResource implementation
304 AppResource::AppResource()
305 : supports_create_(false),
309 AppResource::~AppResource() {}
312 void AppResource::RegisterJSONConverter(
313 base::JSONValueConverter
<AppResource
>* converter
) {
314 converter
->RegisterStringField(kId
, &AppResource::application_id_
);
315 converter
->RegisterStringField(kName
, &AppResource::name_
);
316 converter
->RegisterStringField(kObjectType
, &AppResource::object_type_
);
317 converter
->RegisterStringField(kProductId
, &AppResource::product_id_
);
318 converter
->RegisterBoolField(kSupportsCreate
, &AppResource::supports_create_
);
319 converter
->RegisterBoolField(kRemovable
, &AppResource::removable_
);
320 converter
->RegisterRepeatedString(kPrimaryMimeTypes
,
321 &AppResource::primary_mimetypes_
);
322 converter
->RegisterRepeatedString(kSecondaryMimeTypes
,
323 &AppResource::secondary_mimetypes_
);
324 converter
->RegisterRepeatedString(kPrimaryFileExtensions
,
325 &AppResource::primary_file_extensions_
);
326 converter
->RegisterRepeatedString(kSecondaryFileExtensions
,
327 &AppResource::secondary_file_extensions_
);
328 converter
->RegisterRepeatedMessage(kIcons
, &AppResource::icons_
);
329 converter
->RegisterCustomField
<GURL
>(kCreateUrl
,
330 &AppResource::create_url_
,
335 scoped_ptr
<AppResource
> AppResource::CreateFrom(const base::Value
& value
) {
336 scoped_ptr
<AppResource
> resource(new AppResource());
337 if (!IsResourceKindExpected(value
, kAppKind
) || !resource
->Parse(value
)) {
338 LOG(ERROR
) << "Unable to create: Invalid AppResource JSON!";
339 return scoped_ptr
<AppResource
>();
341 return resource
.Pass();
344 bool AppResource::Parse(const base::Value
& value
) {
345 base::JSONValueConverter
<AppResource
> converter
;
346 if (!converter
.Convert(value
, this)) {
347 LOG(ERROR
) << "Unable to parse: Invalid AppResource";
353 ////////////////////////////////////////////////////////////////////////////////
354 // AppList implementation
356 AppList::AppList() {}
358 AppList::~AppList() {}
361 void AppList::RegisterJSONConverter(
362 base::JSONValueConverter
<AppList
>* converter
) {
363 converter
->RegisterStringField(kETag
, &AppList::etag_
);
364 converter
->RegisterRepeatedMessage
<AppResource
>(kItems
,
369 scoped_ptr
<AppList
> AppList::CreateFrom(const base::Value
& value
) {
370 scoped_ptr
<AppList
> resource(new AppList());
371 if (!IsResourceKindExpected(value
, kAppListKind
) || !resource
->Parse(value
)) {
372 LOG(ERROR
) << "Unable to create: Invalid AppList JSON!";
373 return scoped_ptr
<AppList
>();
375 return resource
.Pass();
378 bool AppList::Parse(const base::Value
& value
) {
379 base::JSONValueConverter
<AppList
> converter
;
380 if (!converter
.Convert(value
, this)) {
381 LOG(ERROR
) << "Unable to parse: Invalid AppList";
387 ////////////////////////////////////////////////////////////////////////////////
388 // ParentReference implementation
390 ParentReference::ParentReference() {}
392 ParentReference::~ParentReference() {}
395 void ParentReference::RegisterJSONConverter(
396 base::JSONValueConverter
<ParentReference
>* converter
) {
397 converter
->RegisterStringField(kId
, &ParentReference::file_id_
);
398 converter
->RegisterCustomField
<GURL
>(kParentLink
,
399 &ParentReference::parent_link_
,
404 scoped_ptr
<ParentReference
>
405 ParentReference::CreateFrom(const base::Value
& value
) {
406 scoped_ptr
<ParentReference
> reference(new ParentReference());
407 if (!IsResourceKindExpected(value
, kParentReferenceKind
) ||
408 !reference
->Parse(value
)) {
409 LOG(ERROR
) << "Unable to create: Invalid ParentRefernce JSON!";
410 return scoped_ptr
<ParentReference
>();
412 return reference
.Pass();
415 bool ParentReference::Parse(const base::Value
& value
) {
416 base::JSONValueConverter
<ParentReference
> converter
;
417 if (!converter
.Convert(value
, this)) {
418 LOG(ERROR
) << "Unable to parse: Invalid ParentReference";
424 ////////////////////////////////////////////////////////////////////////////////
425 // FileResource implementation
427 FileResource::FileResource() : shared_(false), file_size_(kUnsetFileSize
) {}
429 FileResource::~FileResource() {}
432 void FileResource::RegisterJSONConverter(
433 base::JSONValueConverter
<FileResource
>* converter
) {
434 converter
->RegisterStringField(kId
, &FileResource::file_id_
);
435 converter
->RegisterStringField(kETag
, &FileResource::etag_
);
436 converter
->RegisterStringField(kTitle
, &FileResource::title_
);
437 converter
->RegisterStringField(kMimeType
, &FileResource::mime_type_
);
438 converter
->RegisterNestedField(kLabels
, &FileResource::labels_
);
439 converter
->RegisterNestedField(kImageMediaMetadata
,
440 &FileResource::image_media_metadata_
);
441 converter
->RegisterCustomField
<base::Time
>(
443 &FileResource::created_date_
,
444 &util::GetTimeFromString
);
445 converter
->RegisterCustomField
<base::Time
>(
447 &FileResource::modified_date_
,
448 &util::GetTimeFromString
);
449 converter
->RegisterCustomField
<base::Time
>(
451 &FileResource::last_viewed_by_me_date_
,
452 &util::GetTimeFromString
);
453 converter
->RegisterCustomField
<base::Time
>(
455 &FileResource::shared_with_me_date_
,
456 &util::GetTimeFromString
);
457 converter
->RegisterBoolField(kShared
, &FileResource::shared_
);
458 converter
->RegisterStringField(kMd5Checksum
, &FileResource::md5_checksum_
);
459 converter
->RegisterCustomField
<int64
>(kFileSize
,
460 &FileResource::file_size_
,
461 &base::StringToInt64
);
462 converter
->RegisterCustomField
<GURL
>(kAlternateLink
,
463 &FileResource::alternate_link_
,
465 converter
->RegisterCustomValueField
<std::vector
<ParentReference
> >(
467 &FileResource::parents_
,
468 GetParentsFromValue
);
469 converter
->RegisterCustomValueField
<std::vector
<OpenWithLink
> >(
471 &FileResource::open_with_links_
,
472 GetOpenWithLinksFromDictionaryValue
);
476 scoped_ptr
<FileResource
> FileResource::CreateFrom(const base::Value
& value
) {
477 scoped_ptr
<FileResource
> resource(new FileResource());
478 if (!IsResourceKindExpected(value
, kFileKind
) || !resource
->Parse(value
)) {
479 LOG(ERROR
) << "Unable to create: Invalid FileResource JSON!";
480 return scoped_ptr
<FileResource
>();
482 return resource
.Pass();
485 bool FileResource::IsDirectory() const {
486 return mime_type_
== kDriveFolderMimeType
;
489 bool FileResource::IsHostedDocument() const {
490 // Hosted documents don't have fileSize field set:
491 // https://developers.google.com/drive/v2/reference/files
492 return !IsDirectory() && file_size_
== kUnsetFileSize
;
495 bool FileResource::Parse(const base::Value
& value
) {
496 base::JSONValueConverter
<FileResource
> converter
;
497 if (!converter
.Convert(value
, this)) {
498 LOG(ERROR
) << "Unable to parse: Invalid FileResource";
504 ////////////////////////////////////////////////////////////////////////////////
505 // FileList implementation
507 FileList::FileList() {}
509 FileList::~FileList() {}
512 void FileList::RegisterJSONConverter(
513 base::JSONValueConverter
<FileList
>* converter
) {
514 converter
->RegisterCustomField
<GURL
>(kNextLink
,
515 &FileList::next_link_
,
517 converter
->RegisterRepeatedMessage
<FileResource
>(kItems
,
522 bool FileList::HasFileListKind(const base::Value
& value
) {
523 return IsResourceKindExpected(value
, kFileListKind
);
527 scoped_ptr
<FileList
> FileList::CreateFrom(const base::Value
& value
) {
528 scoped_ptr
<FileList
> resource(new FileList());
529 if (!HasFileListKind(value
) || !resource
->Parse(value
)) {
530 LOG(ERROR
) << "Unable to create: Invalid FileList JSON!";
531 return scoped_ptr
<FileList
>();
533 return resource
.Pass();
536 bool FileList::Parse(const base::Value
& value
) {
537 base::JSONValueConverter
<FileList
> converter
;
538 if (!converter
.Convert(value
, this)) {
539 LOG(ERROR
) << "Unable to parse: Invalid FileList";
545 ////////////////////////////////////////////////////////////////////////////////
546 // ChangeResource implementation
548 ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {}
550 ChangeResource::~ChangeResource() {}
553 void ChangeResource::RegisterJSONConverter(
554 base::JSONValueConverter
<ChangeResource
>* converter
) {
555 converter
->RegisterCustomField
<int64
>(kId
,
556 &ChangeResource::change_id_
,
557 &base::StringToInt64
);
558 converter
->RegisterStringField(kFileId
, &ChangeResource::file_id_
);
559 converter
->RegisterBoolField(kDeleted
, &ChangeResource::deleted_
);
560 converter
->RegisterCustomValueField(kFile
, &ChangeResource::file_
,
561 &CreateFileResourceFromValue
);
562 converter
->RegisterCustomField
<base::Time
>(
563 kModificationDate
, &ChangeResource::modification_date_
,
564 &util::GetTimeFromString
);
568 scoped_ptr
<ChangeResource
>
569 ChangeResource::CreateFrom(const base::Value
& value
) {
570 scoped_ptr
<ChangeResource
> resource(new ChangeResource());
571 if (!IsResourceKindExpected(value
, kChangeKind
) || !resource
->Parse(value
)) {
572 LOG(ERROR
) << "Unable to create: Invalid ChangeResource JSON!";
573 return scoped_ptr
<ChangeResource
>();
575 return resource
.Pass();
578 bool ChangeResource::Parse(const base::Value
& value
) {
579 base::JSONValueConverter
<ChangeResource
> converter
;
580 if (!converter
.Convert(value
, this)) {
581 LOG(ERROR
) << "Unable to parse: Invalid ChangeResource";
587 ////////////////////////////////////////////////////////////////////////////////
588 // ChangeList implementation
590 ChangeList::ChangeList() : largest_change_id_(0) {}
592 ChangeList::~ChangeList() {}
595 void ChangeList::RegisterJSONConverter(
596 base::JSONValueConverter
<ChangeList
>* converter
) {
597 converter
->RegisterCustomField
<GURL
>(kNextLink
,
598 &ChangeList::next_link_
,
600 converter
->RegisterCustomField
<int64
>(kLargestChangeId
,
601 &ChangeList::largest_change_id_
,
602 &base::StringToInt64
);
603 converter
->RegisterRepeatedMessage
<ChangeResource
>(kItems
,
604 &ChangeList::items_
);
608 bool ChangeList::HasChangeListKind(const base::Value
& value
) {
609 return IsResourceKindExpected(value
, kChangeListKind
);
613 scoped_ptr
<ChangeList
> ChangeList::CreateFrom(const base::Value
& value
) {
614 scoped_ptr
<ChangeList
> resource(new ChangeList());
615 if (!HasChangeListKind(value
) || !resource
->Parse(value
)) {
616 LOG(ERROR
) << "Unable to create: Invalid ChangeList JSON!";
617 return scoped_ptr
<ChangeList
>();
619 return resource
.Pass();
622 bool ChangeList::Parse(const base::Value
& value
) {
623 base::JSONValueConverter
<ChangeList
> converter
;
624 if (!converter
.Convert(value
, this)) {
625 LOG(ERROR
) << "Unable to parse: Invalid ChangeList";
632 ////////////////////////////////////////////////////////////////////////////////
633 // FileLabels implementation
635 FileLabels::FileLabels() : trashed_(false) {}
637 FileLabels::~FileLabels() {}
640 void FileLabels::RegisterJSONConverter(
641 base::JSONValueConverter
<FileLabels
>* converter
) {
642 converter
->RegisterBoolField(kLabelTrashed
, &FileLabels::trashed_
);
646 scoped_ptr
<FileLabels
> FileLabels::CreateFrom(const base::Value
& value
) {
647 scoped_ptr
<FileLabels
> resource(new FileLabels());
648 if (!resource
->Parse(value
)) {
649 LOG(ERROR
) << "Unable to create: Invalid FileLabels JSON!";
650 return scoped_ptr
<FileLabels
>();
652 return resource
.Pass();
655 bool FileLabels::Parse(const base::Value
& value
) {
656 base::JSONValueConverter
<FileLabels
> converter
;
657 if (!converter
.Convert(value
, this)) {
658 LOG(ERROR
) << "Unable to parse: Invalid FileLabels.";
664 ////////////////////////////////////////////////////////////////////////////////
665 // ImageMediaMetadata implementation
667 ImageMediaMetadata::ImageMediaMetadata()
672 ImageMediaMetadata::~ImageMediaMetadata() {}
675 void ImageMediaMetadata::RegisterJSONConverter(
676 base::JSONValueConverter
<ImageMediaMetadata
>* converter
) {
677 converter
->RegisterIntField(kImageMediaMetadataWidth
,
678 &ImageMediaMetadata::width_
);
679 converter
->RegisterIntField(kImageMediaMetadataHeight
,
680 &ImageMediaMetadata::height_
);
681 converter
->RegisterIntField(kImageMediaMetadataRotation
,
682 &ImageMediaMetadata::rotation_
);
686 scoped_ptr
<ImageMediaMetadata
> ImageMediaMetadata::CreateFrom(
687 const base::Value
& value
) {
688 scoped_ptr
<ImageMediaMetadata
> resource(new ImageMediaMetadata());
689 if (!resource
->Parse(value
)) {
690 LOG(ERROR
) << "Unable to create: Invalid ImageMediaMetadata JSON!";
691 return scoped_ptr
<ImageMediaMetadata
>();
693 return resource
.Pass();
696 bool ImageMediaMetadata::Parse(const base::Value
& value
) {
697 base::JSONValueConverter
<ImageMediaMetadata
> converter
;
698 if (!converter
.Convert(value
, this)) {
699 LOG(ERROR
) << "Unable to parse: Invalid ImageMediaMetadata.";
705 } // namespace google_apis