123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704 |
- //
- // Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License").
- // You may not use this file except in compliance with the License.
- // A copy of the License is located at
- //
- // http://aws.amazon.com/apache2.0
- //
- // or in the "license" file accompanying this file. This file 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.
- //
- #import "AWSSerialization.h"
- #import "AWSXMLWriter.h"
- #import "AWSCategory.h"
- #import "AWSCocoaLumberjack.h"
- #import "AWSXMLDictionary.h"
-
- NSString *const AWSXMLBuilderErrorDomain = @"com.amazonaws.AWSXMLBuilderErrorDomain";
- NSString *const AWSXMLParserErrorDomain = @"com.amazonaws.AWSXMLParserErrorDomain";
- NSString *const AWSQueryParamBuilderErrorDomain = @"com.amazonaws.AWSQueryParamBuilderErrorDomain";
- NSString *const AWSEC2ParamBuilderErrorDomain = @"com.amazonaws.AWSEC2ParamBuilderErrorDomain";
- NSString *const AWSJSONBuilderErrorDomain = @"com.amazonaws.AWSJSONBuilderErrorDomain";
- NSString *const AWSJSONParserErrorDomain = @"com.amazonaws.AWSJSONParserErrorDomain";
- @interface AWSJSONDictionary()
-
- @property (nonatomic, strong) NSDictionary *embeddedDictionary;
- @property (nonatomic, strong) NSDictionary *JSONDefinitionRule;
-
- @end
-
- @implementation AWSJSONDictionary
-
- - (instancetype)initWithDictionary:(NSDictionary *)otherDictionary JSONDefinitionRule:(NSDictionary *)rule {
- self = [super init];
- if (self) {
- _embeddedDictionary = [[NSDictionary alloc] initWithDictionary:otherDictionary];
- _JSONDefinitionRule = [rule copy];
- }
- return self;
- }
-
- - (id)parseResult:(id)result {
- if ([result isKindOfClass:[NSDictionary class]]) {
- return [[AWSJSONDictionary alloc] initWithDictionary:result JSONDefinitionRule:self.JSONDefinitionRule];
- } else {
- return result;
- }
- }
-
- - (NSUInteger)count {
- return [self.embeddedDictionary count];
- }
-
- - (id)objectForKey:(id)aKey {
- //If value found, just return value
- id value = [self.embeddedDictionary objectForKey:aKey];
- if (value) {
- return [self parseResult:value];
- }
-
- //find value in metadata dictionary, return the value if found
- id result = [[self.embeddedDictionary objectForKey:@"metadata"] objectForKey:aKey];
- if (result) {
- return [self parseResult:result];
- }
-
- //find value according to shapeName, return the value if found
- NSString *shapeName = [self.embeddedDictionary objectForKey:@"shape"];
- if (shapeName.length != 0) {
- NSDictionary *definitionResult = [self.JSONDefinitionRule objectForKey:shapeName];
-
- id result = [definitionResult objectForKey:aKey];
- if (result) {
- return [self parseResult:result];
- }
-
- id metaDataResult = [[definitionResult objectForKey:@"metadata"] objectForKey:aKey];
- if (metaDataResult) {
- return [self parseResult:metaDataResult];
- }
- }
-
- return nil;
- }
-
- - (NSEnumerator *)keyEnumerator {
- return [self.embeddedDictionary keyEnumerator];
- }
-
- @end
-
- @implementation AWSXMLBuilder
-
- + (BOOL)failWithCode:(NSInteger)code description:(NSString *)description error:(NSError *__autoreleasing *)error {
- if (error) {
- *error = [NSError errorWithDomain:AWSXMLBuilderErrorDomain
- code:code
- userInfo:@{NSLocalizedDescriptionKey : description}];
- }
- return NO;
- }
-
- + (NSString *)xmlStringForDictionary:(NSDictionary *)params actionName:(NSString *)actionName serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule error:(NSError *__autoreleasing *)error {
- return [[self xmlBuildForDictionary:params actionName:actionName serviceDefinitionRule:serviceDefinitionRule error:error] toString];
- }
-
- + (NSData *)xmlDataForDictionary:(NSDictionary *)params actionName:(NSString *)actionName serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule error:(NSError *__autoreleasing *)error {
-
- if ([params count] == 0) {
- return nil;
- }
- NSData *resultData = [[self xmlBuildForDictionary:params actionName:actionName serviceDefinitionRule:serviceDefinitionRule error:error] toData];
-
- return resultData;
- }
-
- + (AWSXMLWriter *)xmlBuildForDictionary:(NSDictionary *)params actionName:(NSString *)actionName serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule error:(NSError *__autoreleasing *)error {
-
- NSDictionary *actionRule = [[[serviceDefinitionRule objectForKey:@"operations"] objectForKey:actionName] objectForKey:@"input"];
- NSDictionary *definitionRules = [serviceDefinitionRule objectForKey:@"shapes"];
-
- if (definitionRules == (id)[NSNull null] || [definitionRules count] == 0) {
- [self failWithCode:AWSXMLBuilderDefinitionFileIsEmpty description:@"JSON definition File is empty or can not be found" error:error];
- return nil;
- }
-
-
-
- if ([actionRule count] == 0) {
- [self failWithCode:AWSXMLBuilderUndefinedActionRule description:@"Invalid argument: actionRule is Empty" error:error];
- return nil;
- }
-
-
- AWSXMLWriter* xmlWriter = [[AWSXMLWriter alloc]init];
- AWSJSONDictionary *rules = [[AWSJSONDictionary alloc] initWithDictionary:actionRule JSONDefinitionRule:definitionRules];
-
- NSString *xmlElementName = rules[@"locationName"];
- if (xmlElementName) {
- [xmlWriter writeStartElement:xmlElementName];
- [self applyNamespacesAndAttributesByRules:rules params:params xmlWriter:xmlWriter];
- }
-
- [self serializeStructure:params rules:rules xmlWriter:xmlWriter error:error isRootRule:YES];
-
- if (xmlElementName) {
- [xmlWriter writeEndElement:xmlElementName];
- }
-
- return xmlWriter;
- }
-
- + (BOOL)serializeStructure:(NSDictionary *)params rules:(AWSJSONDictionary *)rules xmlWriter:(AWSXMLWriter *)xmlWriter error:(NSError *__autoreleasing *)error isRootRule:(BOOL)isRootRule {
-
- AWSJSONDictionary *structureMembersRule = rules[@"members"]?rules[@"members"]:@{};
-
- //If it is RootRule, only process payload If it exists.
- if (isRootRule) {
- NSString *payloadMemberName = rules[@"payload"];
- if (payloadMemberName) {
- id value = params[payloadMemberName];
- if (value) {
- AWSJSONDictionary *payloadMemberRules = structureMembersRule[payloadMemberName];
- return [self serializeMember:value name:payloadMemberName rules:payloadMemberRules isPayloadType:YES xmlWriter:xmlWriter error:error];
- } else {
- //no payload exists, should return
- return YES;
- }
-
- }
- //if no payload trait, continue to process
- }
-
- __block BOOL isValid = YES;
- __block NSError *blockErr = nil;
- [structureMembersRule enumerateKeysAndObjectsUsingBlock:^(NSString *memberName, id memberRules, BOOL *stop) {
-
- id value = params[memberName];
- if (value) {
-
- if (memberRules[@"xmlAttribute"]) {
- //It should be an attribute, will be proceed in applyNamespacesAndAttributesByRules
- return;
- }
-
- if (memberRules[@"location"]) {
- //It should be another location rather than body, will be process at different place
- return;
- }
-
-
- if (![self serializeMember:value name:memberName rules:memberRules isPayloadType:NO xmlWriter:xmlWriter error:&blockErr]) {
- *stop = YES;
- isValid = NO;
- return;
- }
- }
- }];
- if (error) *error = blockErr;
- return isValid;
- }
-
- + (BOOL)serializeList:(NSArray *)list name:(NSString *)name rules:(AWSJSONDictionary *)rules xmlWriter:(AWSXMLWriter *)xmlWriter error:(NSError *__autoreleasing *)error {
-
- AWSJSONDictionary *memberRules = rules[@"member"]?rules[@"member"]:@{};
- NSString *xmlListName = rules[@"locationName"]?rules[@"locationName"]:name;
-
- __block BOOL isValid = YES;
- __block NSError *blockErr = nil;
- if ([rules[@"flattened"] boolValue]) {
-
- [list enumerateObjectsUsingBlock:^(id value, NSUInteger idx, BOOL *stop) {
- if (![self serializeMember:value name:xmlListName rules:memberRules isPayloadType:NO xmlWriter:xmlWriter error:&blockErr]) {
- *stop = YES;
- isValid = NO;
- return ;
- }
- }];
- } else {
-
- //Add a extra layer for non-flattened format.
- [xmlWriter writeStartElement:xmlListName];
-
- [list enumerateObjectsUsingBlock:^(id value, NSUInteger idx, BOOL *stop) {
- //non-flattened list without locationName should use 'member' as default name
- if (![self serializeMember:value name:@"member" rules:memberRules isPayloadType:NO xmlWriter:xmlWriter error:&blockErr]) {
- *stop = YES;
- isValid = NO;
-
- return ;
- }
-
- }];
-
- [xmlWriter writeEndElement:xmlListName];
- }
- if (error) *error = blockErr;
- return isValid;
- }
-
- + (BOOL)serializeMember:(id)params name:(NSString *)memberName rules:(AWSJSONDictionary *)rules isPayloadType:(Boolean)isPayloadType xmlWriter:(AWSXMLWriter *)xmlWriter error:(NSError *__autoreleasing *)error {
- NSString *xmlElementName = rules[@"locationName"]?rules[@"locationName"]:memberName;
- NSString *rulesType = rules[@"type"];
- if ([rulesType isEqualToString:@"structure"]) {
- [xmlWriter writeStartElement:xmlElementName];
- [self applyNamespacesAndAttributesByRules:rules params:params xmlWriter:xmlWriter];
- [self serializeStructure:params rules:rules xmlWriter:xmlWriter error:error isRootRule:NO];
- [xmlWriter writeEndElement:xmlElementName];
- } else if ([rulesType isEqualToString:@"list"]) {
- [self serializeList:params name:memberName rules:rules xmlWriter:xmlWriter error:error];
- } else if ([rulesType isEqualToString:@"map"]) {
- //TODO: handle map type
- } else if ([rulesType isEqualToString:@"timestamp"]) {
- NSDate *timeStampDate;
- //maybe a NSDate type or NSNumber type or NSString type
- if ([params isKindOfClass:[NSString class]]) {
- //try parse the string to NSDate first
- timeStampDate = [NSDate aws_dateFromString:params];
-
- //if failed, then parse it as double value
- if (!timeStampDate) {
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[params doubleValue]];
- }
- } else if ([params isKindOfClass:[NSNumber class]]) {
- //need to convert to NSDate type
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[params doubleValue]];
-
- } else if ([params isKindOfClass:[NSDate class]]) {
- timeStampDate = params;
- }
-
- //generate string presentation of timestamp
- NSString *timestampStr = @"";
- if ([rules[@"timestampFormat"] isEqualToString:@"iso8601"]) {
- timestampStr = [timeStampDate aws_stringValue:AWSDateISO8601DateFormat1];
- } else if ([rules[@"timestampFormat"] isEqualToString:@"unixTimestamp"]) {
- timestampStr = [NSString stringWithFormat:@"%.lf",[timeStampDate timeIntervalSince1970]];
- } else {
- timestampStr = [timeStampDate aws_stringValue:AWSDateRFC822DateFormat1];
- }
-
-
- if (isPayloadType == NO) [xmlWriter writeStartElement:xmlElementName];
- [xmlWriter writeCharacters:timestampStr];
- if (isPayloadType == NO) [xmlWriter writeEndElement:xmlElementName];
- } else if ([rulesType isEqualToString:@"integer"] || [rulesType isEqualToString:@"long"] || [rulesType isEqualToString:@"float"] || [rulesType isEqualToString:@"double"]) {
- NSNumber *numberValue = params;
- if (isPayloadType == NO) [xmlWriter writeStartElement:xmlElementName];
- [xmlWriter writeCharacters:[numberValue stringValue]];
- if (isPayloadType == NO) [xmlWriter writeEndElement:xmlElementName];
- } else if ([rulesType isEqualToString:@"blob"]) {
- //just handle the non-streaming body, streaming body will be handled in 'constructURIandHeadersAndBody' method
- if ([rules[@"streaming"] boolValue] == NO) {
-
- //encode NSData to Base64String
- if ([params isKindOfClass:[NSString class]]) {
- params = [params dataUsingEncoding:NSUTF8StringEncoding];
- }
- if ([params isKindOfClass:[NSData class]]) {
- if (isPayloadType == NO) {
- NSString *base64encodedStr = [params base64EncodedStringWithOptions:0];
- [xmlWriter writeStartElement:xmlElementName];
- [xmlWriter writeCharacters:base64encodedStr];
- [xmlWriter writeEndElement:xmlElementName];
- } else {
- //Do not base64 encoding if it is payload type
- NSString* utf8String = [[NSString alloc] initWithData:params encoding:NSUTF8StringEncoding];
- [xmlWriter writeCharacters:utf8String?utf8String:@""];
- }
-
- } else {
- [self failWithCode:AWSXMLBuilderInvalidXMLValue description:@"'blob' value should be a NSData type." error:error];
- return NO;
- }
-
- }
-
- } else if ([rulesType isEqualToString:@"boolean"]) {
- if (isPayloadType == NO) [xmlWriter writeStartElement:xmlElementName];
- [xmlWriter writeCharacters:[params boolValue]?@"true":@"false"];
- if (isPayloadType == NO) [xmlWriter writeEndElement:xmlElementName];
- } else if ([rulesType isEqualToString:@"string"]) {
- if (isPayloadType == NO) [xmlWriter writeStartElement:xmlElementName];
- [xmlWriter writeCharacters:params];
- if (isPayloadType == NO) [xmlWriter writeEndElement:xmlElementName];
- } else {
- [self failWithCode:AWSXMLBuilderUnCatchedRuleTypeInDifinitionFile description:[NSString stringWithFormat:@"uncatched ruletype:%@ for value:%@",rulesType,[params description]] error:error];
- return NO;
- }
- return YES;
- }
-
- + (void)applyNamespacesAndAttributesByRules:(NSDictionary *)rules params:(id)params xmlWriter:(AWSXMLWriter *)xmlWriter {
- id xmlNamespaceValue = rules[@"xmlNamespace"];
- if (xmlNamespaceValue) {
- if ([xmlNamespaceValue isKindOfClass:[NSDictionary class]]) {
- NSString *xmlnsName = @"xmlns";
- if (xmlNamespaceValue[@"prefix"]) {
- xmlnsName = [xmlnsName stringByAppendingString:[NSString stringWithFormat:@":%@",xmlNamespaceValue[@"prefix"]]];
- }
- [xmlWriter writeAttribute:xmlnsName value:xmlNamespaceValue[@"uri"]];
- } else if ([xmlNamespaceValue isKindOfClass:[NSString class]]) {
- NSString *xmlnsName = @"xmlns";
- [xmlWriter writeAttribute:xmlnsName value:xmlNamespaceValue];
- }
- }
-
- if ([rules[@"members"][@"Type"][@"xmlAttribute"] boolValue]) {
- NSString *xmlName = rules[@"members"][@"Type"][@"locationName"];
- if (params[@"Type"]) {
- [xmlWriter writeAttribute:xmlName value:params[@"Type"]];
- }
- }
- }
-
- @end
-
- @interface AWSXMLParser ()
-
- @property (nonatomic, strong) AWSXMLDictionaryParser *xmlDictionaryParser;
-
- @end
-
- @implementation AWSXMLParser
-
- + (AWSXMLParser *)sharedInstance {
- static dispatch_once_t once;
- static AWSXMLParser *sharedInstance;
- dispatch_once(&once, ^{
- sharedInstance = [AWSXMLParser new];
- });
- return sharedInstance;
- }
-
- - (instancetype)init {
- if (self = [super init]) {
- _xmlDictionaryParser = [AWSXMLDictionaryParser new];
- _xmlDictionaryParser.trimWhiteSpace = NO;
- _xmlDictionaryParser.attributesMode = AWSXMLDictionaryAttributesModeDiscard; //discard all xml attributes. e.g. xmlns
- _xmlDictionaryParser.stripEmptyNodes = NO;
- _xmlDictionaryParser.wrapRootNode = YES; //wrapRootNode for easy process
- _xmlDictionaryParser.nodeNameMode = AWSXMLDictionaryNodeNameModeNever; //do not need rootName anymore since rootNode is wrapped.
- }
-
- return self;
- }
-
- + (BOOL)failWithCode:(NSInteger)code description:(NSString *)description error:(NSError *__autoreleasing *)error {
- if (error) {
- *error = [NSError errorWithDomain:AWSXMLParserErrorDomain
- code:code
- userInfo:@{NSLocalizedDescriptionKey : description}];
- }
- return NO;
- }
-
- + (NSMutableDictionary *)preprocessDictionary:(NSMutableDictionary *)fromDictionary operationName:(NSString *)operationName actionRule:(NSDictionary *)actionRule serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule {
-
- NSString *serviceTypeStr = serviceDefinitionRule[@"metadata"][@"type"]?serviceDefinitionRule[@"metadata"][@"type"]:serviceDefinitionRule[@"metadata"][@"protocol"];
-
-
- if ([serviceTypeStr isEqualToString:@"query"]) {
- NSNumber *isResultWrapped = serviceDefinitionRule[@"metadata"][@"resultWrapped"];
- if (isResultWrapped && ![isResultWrapped boolValue]) {
- //If resultWrapped is false
- return fromDictionary;
- } else {
- //If not set, it is true by default
- if (actionRule[@"resultWrapper"] && fromDictionary[actionRule[@"resultWrapper"]]) {
- return fromDictionary[actionRule[@"resultWrapper"]];
- } else if ([operationName stringByAppendingString:@"Result"] && fromDictionary[[operationName stringByAppendingString:@"Result"]]){
- return fromDictionary[[operationName stringByAppendingString:@"Result"]];
- } else {
- return fromDictionary;
- }
- }
-
- } else if ([serviceTypeStr isEqualToString:@"rest-xml"]) {
- //TODO: handle rest-xml type for Route 53 and S3
- return fromDictionary;
- } else if ([serviceTypeStr isEqualToString:@"json"]) {
- return fromDictionary;
- } else if ([serviceTypeStr isEqualToString:@"rest-json"]) {
- return fromDictionary;
- } else {
- return fromDictionary;
- }
- }
-
- - (NSMutableDictionary *)dictionaryForXMLData:(NSData *)data
- actionName:(NSString *)actionName
- serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule
- error:(NSError *__autoreleasing *)error {
- if (!data) {
- return [NSMutableDictionary new];
- }
-
- NSDictionary *actionRule = [[[serviceDefinitionRule objectForKey:@"operations"] objectForKey:actionName] objectForKey:@"output"];
- if (actionRule == (id)[NSNull null]) {
- actionRule = @{};
- }
-
- NSDictionary *definitionRules = [serviceDefinitionRule objectForKey:@"shapes"];
- if (definitionRules == (id)[NSNull null]) {
- definitionRules = @{};
- }
- if ([definitionRules count] == 0) {
- if (error) {
- *error = [NSError errorWithDomain:AWSXMLParserErrorDomain
- code:AWSXMLParserDefinitionFileIsEmpty
- userInfo:@{
- NSLocalizedDescriptionKey : @"JSON definition File is empty or can not be found"
- }];
- }
- return nil;
- }
-
- NSMutableDictionary *rootXmlDictionary = nil;
- if ([data isKindOfClass:[NSData class]]) {
- @synchronized (self) {
- rootXmlDictionary = [[self.xmlDictionaryParser dictionaryWithData:data] mutableCopy]; //TODO: need error parameters for parsing
- }
- }
-
- NSString *rootNodeName = [[rootXmlDictionary allKeys] firstObject];
-
- NSMutableDictionary *xmlDictionary = ([rootXmlDictionary[rootNodeName] isKindOfClass:[NSDictionary class]] && [rootXmlDictionary[rootNodeName] count] > 0)?rootXmlDictionary[rootNodeName]:rootXmlDictionary;
-
- if (*error) {
- return nil;
- } else if ([rootNodeName isEqualToString:@"Error"]) {
- //This is an S3 error response, just return parsed xmlDictionary.
- return [@{rootNodeName:xmlDictionary} mutableCopy];
- } else if ([xmlDictionary objectForKey:@"Errors"]) {
- //This is EC2 error response.
- if ([[xmlDictionary objectForKey:@"Errors"] isKindOfClass:[NSDictionary class]]) {
- return [xmlDictionary objectForKey:@"Errors"];
- } else if ([[xmlDictionary objectForKey:@"Errors"] isKindOfClass:[NSArray class]]) {
- return [[xmlDictionary objectForKey:@"Errors"] firstObject];
- }
- return nil;
- }else if ([xmlDictionary objectForKey:@"Error"]) {
- //This is mostly used error response, return xmlDictionary
- return [xmlDictionary mutableCopy];
- }else {
- AWSJSONDictionary *rules = [[AWSJSONDictionary alloc] initWithDictionary:actionRule JSONDefinitionRule:definitionRules];
-
- xmlDictionary = [AWSXMLParser preprocessDictionary:xmlDictionary operationName:actionName actionRule:rules serviceDefinitionRule:serviceDefinitionRule];
-
- NSString *isPayloadData = rules[@"payload"];
- rules = rules[@"members"]?rules[@"members"]:@{};
- NSMutableDictionary *parsedData = [NSMutableDictionary new];
-
- if (isPayloadData) {
- //check if it is streaming type
- if (rules[isPayloadData][@"streaming"]) {
- parsedData[isPayloadData] = data;
- return parsedData;
- }
-
- rules = rules[isPayloadData][@"members"];
- parsedData[isPayloadData] = [AWSXMLParser parseStructure:xmlDictionary rules:rules error:error];
- } else {
- parsedData = [AWSXMLParser parseStructure:xmlDictionary rules:rules error:error];
- }
-
-
- if ([parsedData count] == 0) {
- //try again with rootDictionary if it is S3 response
- NSString *serviceTypeStr = serviceDefinitionRule[@"metadata"][@"type"]?serviceDefinitionRule[@"metadata"][@"type"]:serviceDefinitionRule[@"metadata"][@"protocol"];
- if ([serviceTypeStr isEqualToString:@"rest-xml"]) {
- xmlDictionary = [AWSXMLParser preprocessDictionary:xmlDictionary operationName:actionName actionRule:actionRule serviceDefinitionRule:serviceDefinitionRule];
- parsedData = [AWSXMLParser parseStructure:xmlDictionary rules:rules error:error];
- }
- }
-
- return parsedData;
- };
- }
-
- + (NSString *)findKeyNameByXMLName:(NSString *)xmlName rules:(NSDictionary *)rules {
- __block NSString *result;
- [rules enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
-
- if ([key isEqualToString:xmlName]) {
- result = key;
- *stop = YES;
- return;
- }
-
- if ([obj isKindOfClass:[NSDictionary class]] && ([obj[@"type"] isEqualToString:@"list"] || [obj[@"type"] isEqualToString:@"map"])) {
- if ([obj[@"flattened"] boolValue]) {
- NSString *objXMLName = obj[@"member"][@"locationName"]?obj[@"member"][@"locationName"]:obj[@"locationName"];
- objXMLName = objXMLName?objXMLName:@"member";
- if ([xmlName isEqualToString:objXMLName]) {
- result = key;
- *stop = YES;
- return;
- }
- } else {
- if ([xmlName isEqualToString:obj[@"locationName"]]) {
- result = key;
- *stop = YES;
- return;
- }
- }
-
- }
-
- if ([obj isKindOfClass:[NSDictionary class]] && [obj objectForKey:@"locationName"]) {
- if ([xmlName isEqualToString:[obj objectForKey:@"locationName"]]) {
- result = key;
- *stop = YES;
- return;
- }
-
- }
-
-
-
-
- }];
- return result;
- }
-
- + (BOOL)validateConstraint:(id)value rules:(NSDictionary *)rules error:(NSError *__autoreleasing *)error {
-
- // //validate the existence of required parameters.
- // while ([rules[@"required"] boolValue]) {
- // //value is a structure or map
- // if ([value isKindOfClass:[NSDictionary class]] && [value count]>0) break;
- // //value is a list
- // if ([value isKindOfClass:[NSArray class]] && [value count]>0) break;
- // //value is a string
- // if ([value isKindOfClass:[NSString class]] && [value length] > 0) break;
- // //value is NSNumber( e.g long, integer, double, float)
- // if ([value isKindOfClass:[NSNumber class]]) break;
- //
- // return [self failWithCode:AWSXMLParserMissingRequiredXMLElements
- // description:[NSString stringWithFormat:@"Missing required key."]
- // error:error];
- // }
- //
- // //validate value of string according to enum list
- // if (rules[@"enum"] && [value isKindOfClass:[NSString class]]) {
- // NSArray *enumArray = rules[@"enum"];
- // if (![enumArray containsObject:value]) {
- // return [self failWithCode:AWSXMLParserInvalidXMLValue
- // description:[NSString stringWithFormat:@"got unexpected string:%@ not in the enum list:%@", value, [enumArray description]]
- // error:error];
- // }
- // }
- return YES;
- }
-
-
- + (NSMutableDictionary *)parseStructure:(NSDictionary *)structure rules:(AWSJSONDictionary *)rules error:(NSError *__autoreleasing *)error {
- NSMutableDictionary *data = [NSMutableDictionary dictionary];
-
- if (![self validateConstraint:structure rules:rules error:error]) {
- return data;
- }
-
- __block NSError *blockErr = nil;
- [structure enumerateKeysAndObjectsUsingBlock:^(NSString *xmlName, id value, BOOL *stop) {
- if ([xmlName isEqualToString:@"$"]) {
- } else {
- NSString *keyName = [self findKeyNameByXMLName:xmlName rules:rules];
- if (!keyName) {
- if (![xmlName isEqualToString:@"_xmlns"] &&
- ![xmlName isEqualToString:@"requestId"] &&
- ![xmlName isEqualToString:@"ResponseMetadata"] &&
- ![xmlName isEqualToString:@"__text"]) {
- AWSDDLogWarn(@"Response element ignored: no rule for %@ - %@", xmlName, [value description]);
- }
-
- /*[self failWithCode:AWSXMLParserXMLNameNotFoundInDefinition
- description:[NSString stringWithFormat:@"Can not find the xmlName:%@ in definition to validate xml data", xmlName]
- error:&blockErr];
- *stop = YES;
- */
- return;
- }
- AWSJSONDictionary *rule = rules[keyName];
- if ([rules count] == 0) {
- [self failWithCode:AWSXMLParserUnexpectedXMLElement description:[NSString stringWithFormat:@"Unexpected XML Element found:%@",xmlName] error:&blockErr];
- *stop = YES;
- } else {
- NSString *dicName = rule[@"name"]?rule[@"name"]:keyName;
- data[dicName] = [self parseMember:value rules:rule error:&blockErr];
- if (blockErr) *stop = YES;
- }
-
- }
- }];
-
- if (error) *error = blockErr;
-
- return data;
- }
-
- + (NSMutableDictionary *)parseMap:(id)map rules:(AWSJSONDictionary *)rules error:(NSError *__autoreleasing *)error {
- AWSJSONDictionary *keyRules = rules[@"key"]?rules[@"key"]:@{};
- AWSJSONDictionary *valueRules = rules[@"value"]?rules[@"value"]:@{};
- NSString *keyName = keyRules[@"locationName"]?keyRules[@"locationName"]:@"key";
- NSString *valueName = valueRules[@"locationName"]?valueRules[@"locationName"]:@"value";
-
- __block NSMutableDictionary *data = [NSMutableDictionary dictionary];
-
- if (![self validateConstraint:map rules:rules error:error]) return data;
-
- NSArray *mapList = nil;
- if ([rules[@"flattened"] boolValue] == NO) {
- //If it is non-flatened map,retrive the array with key 'entry' if it has one
- if ([map isKindOfClass:[NSDictionary class]] && [map objectForKey:@"entry"]) {
- mapList = [map objectForKey:@"entry"];
- } else {
- mapList = map;
- }
- } else {
- mapList = map;
- }
-
-
- //if no content, return empty dictionary
- if (!mapList) {
- return data;
- }
- // if 'map' array has only one entry, it will be treat as dictionary, we need to add a array wrapper for that.
- if ([mapList isKindOfClass:[NSDictionary class]]) {
- mapList = @[mapList];
- }
-
- if (![mapList isKindOfClass:[NSArray class]]) {
- [self failWithCode:AWSXMLParserUnExpectedType description:[NSString stringWithFormat:@"xml(mapList type) should be an array but got:%@",NSStringFromClass([mapList class])] error:error];
- return [NSMutableDictionary new];
- } else {
- __block NSError *blockErr = nil;
- [mapList enumerateObjectsUsingBlock:^(id entry, NSUInteger idx, BOOL *stop) {
- NSString *dataKeyName = entry[keyName];
- if (dataKeyName) {
- data[dataKeyName] = [self parseMember:entry[valueName] rules:valueRules error:&blockErr];
- if (blockErr) *stop = YES;
- }
- }];
- if (error) *error = blockErr;
- return data;
- }
- }
-
- + (NSArray *)parseList:(id)list rules:(AWSJSONDictionary *)rules error:(NSError *__autoreleasing *)error {
-
- AWSJSONDictionary *memberRules = rules[@"member"]?rules[@"member"]:@{};
- __block NSMutableArray *data = [NSMutableArray array];
-
- if (![self validateConstraint:list rules:rules error:error]) return data;
- __block NSError *blockErr = nil;
-
- //If not flattened, need to manually flatten it.
- if (![rules[@"flattened"] boolValue]) {
- NSString *memberName = memberRules[@"locationName"]?memberRules[@"locationName"]:@"member";
- if (![list isKindOfClass:[NSDictionary class]]) {
- [self failWithCode:AWSXMLParserUnExpectedType description:[NSString stringWithFormat:@"unflattened xml(list type) should be dictionary but got:%@",NSStringFromClass([list class])] error:error];
- return @[];
- }
- if ([list count] == 0) {
- return @[];
- }
-
- list = list[memberName];
- if (!list) {
- [self failWithCode:AWSXMLParserUnExpectedType description:[NSString stringWithFormat:@"Can not find the '%@' key-pair in un-falttened xml list type",memberName] error:error];
- return @[@"XMLPARSER:ERROR"];
- }
- }
-
- if ([list isKindOfClass:[NSDictionary class]]) {
- // if 'list' isn't an array but a dictionary, we create a new array containing our object.
- list = @[list];
- }
-
- if (![list isKindOfClass:[NSArray class]]) {
- return @[list];
- }
- [list enumerateObjectsUsingBlock:^(id value, NSUInteger idx, BOOL *stop) {
- [data addObject:[self parseMember:value rules:memberRules error:&blockErr]];
- if (blockErr) *stop = YES;
- }];
- if (error) *error = blockErr;
- return data;
- }
-
- + (id)parseMember:(id)values rules:(AWSJSONDictionary *)rules error:(NSError *__autoreleasing *)error {
-
- NSString *rulesType = rules[@"type"];
-
- //if value is nil, return error or empty array/dictionary.
- if (!values) {
- if ([rulesType isEqualToString:@"structure"]) return @{};
- if ([rulesType isEqualToString:@"list"]) return @[];
- if ([rulesType isEqualToString:@"map"]) return @{};
- return @"XMLPARSER:ERROR";
- }
-
- //if there is no 'type' key in rules, return nil with error
- if (!rulesType) {
- [self failWithCode:AWSXMLParserNoTypeDefinitionInRule description:[NSString stringWithFormat:@"can not find the 'type' keywords in definition file:%@ for value:%@",[rules description],[values description]] error:error];
- return @"XMLPARSER:ERROR";
- }
-
- //validate the value
- if (![self validateConstraint:values rules:rules error:error]) return @"XMLPARSER:ERROR";
-
- if ([rulesType isEqualToString:@"string"] || [rulesType isEqualToString:@"character"]) {
- if ([values isKindOfClass:[NSString class]]) {
- return values;
- } else if ([values isKindOfClass:[NSDictionary class]] && [values count] == 0) {
- return @"";
- } else {
- return [values description];
- }
- } else if ([rulesType isEqualToString:@"structure"]) {
- return [self parseStructure:values rules:rules[@"members"]?rules[@"members"]:@{} error:error];
- } else if ([rulesType isEqualToString:@"list"]) {
- return [self parseList:values rules:rules error:error];
- } else if ([rulesType isEqualToString:@"map"]) {
- return [self parseMap:values rules:rules error:error];
- } else if ([rulesType isEqualToString:@"integer"] || [rulesType isEqualToString:@"long"]) {
- if ([values isKindOfClass:[NSNumber class]]) {
- return values;
- } else if ([values isKindOfClass:[NSString class]]) {
- return [NSNumber numberWithInteger:[values integerValue]];
- }
-
- } else if ([rulesType isEqualToString:@"float"] || [rulesType isEqualToString:@"double"]) {
- if ([values isKindOfClass:[NSNumber class]]) {
- return values;
- } else if ([values isKindOfClass:[NSString class]]) {
- return [NSNumber numberWithDouble:[values doubleValue]];
- }
- } else if ([rulesType isEqualToString:@"boolean"]) {
- if ([values isKindOfClass:[NSNumber class]]) {
- return values;
- } else if ([values isKindOfClass:[NSString class]]) {
- return [NSNumber numberWithBool:[values boolValue]];
- }
- } else if ([rulesType isEqualToString:@"timestamp"]) {
- //a value with NSNumber type should be a good timestamp.
- NSDate *timeStampDate;
- //maybe a NSDate type or NSNumber type or NSString type
- if ([values isKindOfClass:[NSString class]]) {
- //try parse the string to NSDate first
- timeStampDate = [NSDate aws_dateFromString:values];
-
- //if failed, then parse it as double value
- if (!timeStampDate) {
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[values doubleValue]];
- }
- } else if ([values isKindOfClass:[NSNumber class]]) {
- //need to convert to NSDate type
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[values doubleValue]];
-
- } else if ([values isKindOfClass:[NSDate class]]) {
- timeStampDate = values;
- }
-
- //generate string presentation of timestamp
- NSString *timestampStr = @"";
- if ([rules[@"timestampFormat"] isEqualToString:@"iso8601"]) {
- timestampStr = [timeStampDate aws_stringValue:AWSDateISO8601DateFormat1];
- } else if ([rules[@"timestampFormat"] isEqualToString:@"unixTimestamp"]) {
- timestampStr = [NSString stringWithFormat:@"%.lf",[timeStampDate timeIntervalSince1970]];
- } else {
- timestampStr = [timeStampDate aws_stringValue:AWSDateISO8601DateFormat1];
- }
-
- return timestampStr;
-
- } else if ([rulesType isEqualToString:@"blob"]) {
-
- //decode Base64Str to NSData
- if ([values isKindOfClass:[NSString class]]) {
- NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:values options:0];
- //return origin string value if can not be encoded.
- return decodedData?decodedData:values;
- } else {
- [self failWithCode:AWSXMLParserInvalidXMLValue description:@"blob value should be NSString type" error:error];
- return [NSData new];
- }
- }
-
- [self failWithCode:AWSXMLParserUnHandledType description:[NSString stringWithFormat:@"unhandled type for value:%@",[values description]] error:error];
- return @"XMLPARSER:ERROR";
- }
-
- @end
-
-
- @implementation AWSQueryParamBuilder
-
- + (BOOL)failWithCode:(NSInteger)code description:(NSString *)description error:(NSError *__autoreleasing *)error {
- if (error) {
- *error = [NSError errorWithDomain:AWSQueryParamBuilderErrorDomain
- code:code
- userInfo:@{NSLocalizedDescriptionKey : description}];
- }
- return NO;
- }
-
- + (NSDictionary *)buildFormattedParams:(NSDictionary *)params
- actionName:(NSString *)actionName
- serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule
- error:(NSError *__autoreleasing *)error {
-
- NSMutableDictionary *formattedParams = [NSMutableDictionary new];
-
-
- //add ActionName
- NSString *urlEncodedActionName = [actionName aws_stringWithURLEncoding];
- if (!urlEncodedActionName) {
- AWSDDLogError(@"actionName is nil!");
- [self failWithCode:AWSQueryParamBuilderUndefinedActionRule description:@"actionName is nil" error:error];
- return nil;
- }
-
- [formattedParams setObject:urlEncodedActionName forKey:@"Action"];
-
-
- //add Version Number
- if (serviceDefinitionRule[@"metadata"] && serviceDefinitionRule[@"metadata"][@"apiVersion"] && [serviceDefinitionRule[@"metadata"][@"apiVersion"] isKindOfClass:[NSString class]]) {
- NSString *urlEncodedAPIVersion = [serviceDefinitionRule[@"metadata"][@"apiVersion"] aws_stringWithURLEncoding];
- if (urlEncodedAPIVersion) {
- [formattedParams setObject:urlEncodedAPIVersion forKey:@"Version"];
- } else {
- AWSDDLogError(@"can not encode APIVersion String:%@",urlEncodedAPIVersion);
- }
-
- } else {
- AWSDDLogError(@"can not find apiVersion keyword in definition file!");
- }
-
- if ([params count] == 0) {
- return formattedParams;
- }
-
- //add params
- NSDictionary *actionRule = [[[serviceDefinitionRule objectForKey:@"operations"] objectForKey:actionName] objectForKey:@"input"];
- NSDictionary *definitionRules = [serviceDefinitionRule objectForKey:@"shapes"];
-
- if (definitionRules == (id)[NSNull null] || [definitionRules count] == 0) {
- [self failWithCode:AWSQueryParamBuilderDefinitionFileIsEmpty description:@"JSON definition File is empty or can not be found" error:error];
- return nil;
- }
-
-
-
- if ([actionRule count] == 0) {
- [self failWithCode:AWSQueryParamBuilderUndefinedActionRule description:@"Invalid argument: actionRule is Empty" error:error];
- return nil;
- }
-
- AWSJSONDictionary *rules = [[AWSJSONDictionary alloc] initWithDictionary:actionRule JSONDefinitionRule:definitionRules];
-
-
- [AWSQueryParamBuilder serializeStructure:params rules:rules prefix:@"" formattedParams:formattedParams error:error];
-
-
- return formattedParams;
-
- }
-
- + (BOOL)serializeStructure:(NSDictionary *)values rules:(AWSJSONDictionary *)structureRules prefix:(NSString *)prefix formattedParams:(NSMutableDictionary *)formattedParams error:(NSError *__autoreleasing *)error {
-
- for (NSString *name in values) {
- id value = values[name];
-
- AWSJSONDictionary *memberShape = structureRules[@"members"][name];
- if (memberShape && value) {
- [self serializeMember:value rules:memberShape prefix:[NSString stringWithFormat:@"%@%@",prefix,[self queryName:memberShape withDefaultName:name]] formattedParams:formattedParams error:error];
- if (error && *error != nil) {
- return NO;
- }
- }
- }
- return YES;
- }
-
- + (BOOL)serializeList:(NSArray *)values rules:(AWSJSONDictionary *)listRules prefix:(NSString *)prefix formattedParams:(NSMutableDictionary *)formattedParams error:(NSError *__autoreleasing *)error {
- if (values == nil) {
- if (prefix) {
- [formattedParams setObject:prefix forKey:@""];
- }
- return YES;
- }
-
- if ([listRules[@"flattened"] boolValue]) {
- NSString *memberName = [self queryName:listRules[@"member"] withDefaultName:nil];
- if (memberName) {
- //substitute memberName
- NSMutableArray *parts = [[prefix componentsSeparatedByString:@"."] mutableCopy];
- if (parts) {
- [parts removeLastObject];
- [parts addObject:memberName];
- prefix = [[parts componentsJoinedByString:@"."] mutableCopy];
- }
- }
-
- } else {
- prefix = [prefix stringByAppendingString:@".member"];
- }
-
- for (int i = 0; i < [values count]; i++) {
- id value = values[i];
- [self serializeMember:value rules:listRules[@"member"] prefix:[NSString stringWithFormat:@"%@.%d",prefix,i+1] formattedParams:formattedParams error:error];
- if (error && *error != nil) {
- return NO;
- }
- }
-
- return YES;
- }
-
- + (BOOL)serializeMap:(NSDictionary *)values rules:(AWSJSONDictionary *)mapRules prefix:(NSString *)prefix formattedParams:(NSMutableDictionary *)formattedParams error:(NSError *__autoreleasing *)error {
- if ([mapRules[@"flattened"] boolValue] == NO) {
- prefix = [prefix stringByAppendingString:@".entry"];
- }
-
- NSArray *allKeysArray = [[values allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
- int index = 0;
- for (NSString *key in allKeysArray) {
- id value = values[key];
- [self serializeMember:key rules:mapRules[@"key"] prefix:[NSString stringWithFormat:@"%@.%d.%@",prefix,index+1,[self queryName:mapRules[@"key"] withDefaultName:@"key"]] formattedParams:formattedParams error:error];
-
- if (error && *error != nil) return NO;
-
- [self serializeMember:value rules:mapRules[@"value"] prefix:[NSString stringWithFormat:@"%@.%d.%@",prefix,index+1,[self queryName:mapRules[@"value"] withDefaultName:@"value"]] formattedParams:formattedParams error:error];
-
- if (error && *error != nil) return NO;
- index++;
- }
-
- return YES;
- }
-
- + (BOOL)serializeMember:(id)value rules:(AWSJSONDictionary *)shape prefix:(NSString *)prefix formattedParams:(NSMutableDictionary *)formattedParams error:(NSError *__autoreleasing *)error {
-
- if (prefix == nil) {
- prefix = @"";
- }
-
- NSString *rulesType = shape[@"type"];
- if ([rulesType isEqualToString:@"structure"]) {
- [self serializeStructure:value rules:shape prefix:[NSString stringWithFormat:@"%@.",prefix] formattedParams:formattedParams error:error];
- } else if ([rulesType isEqualToString:@"list"]) {
- [self serializeList:value rules:shape prefix:prefix formattedParams:formattedParams error:error];
- } else if ([rulesType isEqualToString:@"map"]) {
- [self serializeMap:value rules:shape prefix:prefix formattedParams:formattedParams error:error];
- } else if ([rulesType isEqualToString:@"timestamp"]) {
- NSDate *timeStampDate;
- //maybe a NSDate type or NSNumber type or NSString type
- if ([value isKindOfClass:[NSString class]]) {
- //try parse the string to NSDate first
- timeStampDate = [NSDate aws_dateFromString:value];
-
- //if failed, then parse it as double value
- if (!timeStampDate) {
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
- }
- } else if ([value isKindOfClass:[NSNumber class]]) {
- //need to convert to NSDate type
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
-
- } else if ([value isKindOfClass:[NSDate class]]) {
- timeStampDate = value;
- }
-
- //generate string presentation of timestamp
- //generate string presentation of timestamp
- NSString *timestampStr = @"";
- if ([shape[@"timestampFormat"] isEqualToString:@"iso8601"]) {
- timestampStr = [timeStampDate aws_stringValue:AWSDateISO8601DateFormat1];
- } else if ([shape[@"timestampFormat"] isEqualToString:@"unixTimestamp"]) {
- timestampStr = [NSString stringWithFormat:@"%.lf",[timeStampDate timeIntervalSince1970]];
- } else {
- //default timeStamp format
- timestampStr = [timeStampDate aws_stringValue:AWSDateISO8601DateFormat1];
- }
-
- if (!timestampStr) {
- timestampStr = @"";
- }
-
- formattedParams[prefix] = timestampStr;
-
- } else if ([rulesType isEqualToString:@"blob"]) {
-
- //encode NSData to Base64String
- if ([value isKindOfClass:[NSString class]]) {
- value = [value dataUsingEncoding:NSUTF8StringEncoding];
- }
- if ([value isKindOfClass:[NSData class]]) {
- NSString *base64encodedStr = [value base64EncodedStringWithOptions:0];
- formattedParams[prefix] = base64encodedStr?base64encodedStr:@"";
- } else {
- [self failWithCode:AWSQueryParamBuilderInvalidParameter description:@"'blob' value should be a NSData type." error:error];
- return NO;
- }
-
- } else if ([rulesType isEqualToString:@"boolean"]) {
- formattedParams[prefix] = [value boolValue]?@"true":@"false";
- } else {
- formattedParams[prefix] = value;
- }
-
- return YES;
- }
-
- + (NSString *)queryName:(NSDictionary *)shape withDefaultName:(NSString *)defaultName {
-
- return shape[@"locationName"]?shape[@"locationName"]:defaultName;
- }
- @end
-
- @implementation AWSEC2ParamBuilder
-
- + (BOOL)failWithCode:(NSInteger)code description:(NSString *)description error:(NSError *__autoreleasing *)error {
- if (error) {
- *error = [NSError errorWithDomain:AWSEC2ParamBuilderErrorDomain
- code:code
- userInfo:@{NSLocalizedDescriptionKey : description}];
- }
- return NO;
- }
-
- + (NSDictionary *)buildFormattedParams:(NSDictionary *)params
- actionName:(NSString *)actionName
- serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule
- error:(NSError *__autoreleasing *)error {
- NSMutableDictionary *formattedParams = [NSMutableDictionary new];
-
-
- //add ActionName
- NSString *urlEncodedActionName = [actionName aws_stringWithURLEncoding];
- if (!urlEncodedActionName) {
- AWSDDLogError(@"actionName is nil!");
- [self failWithCode:AWSEC2ParamBuilderUndefinedActionRule description:@"actionName is nil" error:error];
- return nil;
- }
-
- [formattedParams setObject:urlEncodedActionName forKey:@"Action"];
-
-
- //add Version Number
- if (serviceDefinitionRule[@"metadata"] && serviceDefinitionRule[@"metadata"][@"apiVersion"] && [serviceDefinitionRule[@"metadata"][@"apiVersion"] isKindOfClass:[NSString class]]) {
- NSString *urlEncodedAPIVersion = [serviceDefinitionRule[@"metadata"][@"apiVersion"] aws_stringWithURLEncoding];
- if (urlEncodedAPIVersion) {
- [formattedParams setObject:urlEncodedAPIVersion forKey:@"Version"];
- } else {
- AWSDDLogError(@"can not encode APIVersion String:%@",urlEncodedAPIVersion);
- }
-
- } else {
- AWSDDLogError(@"can not find apiVersion keyword in definition file!");
- }
-
- if ([params count] == 0) {
- return formattedParams;
- }
-
- //add params
- NSDictionary *actionRule = [[[serviceDefinitionRule objectForKey:@"operations"] objectForKey:actionName] objectForKey:@"input"];
- NSDictionary *definitionRules = [serviceDefinitionRule objectForKey:@"shapes"];
-
- if (definitionRules == (id)[NSNull null] || [definitionRules count] == 0) {
- [self failWithCode:AWSEC2ParamBuilderDefinitionFileIsEmpty description:@"JSON definition File is empty or can not be found" error:error];
- return nil;
- }
-
-
-
- if ([actionRule count] == 0) {
- [self failWithCode:AWSEC2ParamBuilderUndefinedActionRule description:@"Invalid argument: actionRule is Empty" error:error];
- return nil;
- }
-
- AWSJSONDictionary *rules = [[AWSJSONDictionary alloc] initWithDictionary:actionRule JSONDefinitionRule:definitionRules];
-
-
- [AWSEC2ParamBuilder serializeStructure:params rules:rules prefix:@"" formattedParams:formattedParams error:error];
-
-
- return formattedParams;
-
- }
-
- + (BOOL)serializeStructure:(NSDictionary *)values rules:(AWSJSONDictionary *)structureRules prefix:(NSString *)prefix formattedParams:(NSMutableDictionary *)formattedParams error:(NSError *__autoreleasing *)error {
-
- for (NSString *name in values) {
- id value = values[name];
-
- AWSJSONDictionary *memberShape = structureRules[@"members"][name];
- if (memberShape && value) {
- [self serializeMember:value rules:memberShape prefix:[NSString stringWithFormat:@"%@%@",prefix,[self queryName:memberShape withDefaultName:name]] formattedParams:formattedParams error:error];
- if (error && *error != nil) {
- return NO;
- }
- }
- }
- return YES;
- }
-
- + (BOOL)serializeList:(NSArray *)values rules:(AWSJSONDictionary *)listRules prefix:(NSString *)prefix formattedParams:(NSMutableDictionary *)formattedParams error:(NSError *__autoreleasing *)error {
- if (values == nil) {
- if (prefix) {
- [formattedParams setObject:prefix forKey:@""];
- }
- return YES;
- }
-
- for (int i = 0; i < [values count]; i++) {
- id value = values[i];
- [self serializeMember:value rules:listRules[@"member"] prefix:[NSString stringWithFormat:@"%@.%d",prefix,i+1] formattedParams:formattedParams error:error];
- if (error && *error != nil) {
- return NO;
- }
- }
-
- return YES;
- }
-
- + (BOOL)serializeMember:(id)value rules:(AWSJSONDictionary *)shape prefix:(NSString *)prefix formattedParams:(NSMutableDictionary *)formattedParams error:(NSError *__autoreleasing *)error {
-
- if (prefix == nil) {
- prefix = @"";
- }
-
- NSString *rulesType = shape[@"type"];
- if ([rulesType isEqualToString:@"structure"]) {
- [self serializeStructure:value rules:shape prefix:[NSString stringWithFormat:@"%@.",prefix] formattedParams:formattedParams error:error];
- } else if ([rulesType isEqualToString:@"list"]) {
- [self serializeList:value rules:shape prefix:prefix formattedParams:formattedParams error:error];
- } else if ([rulesType isEqualToString:@"map"]) {
- // EC2 does not have any map type yet
- [self failWithCode:AWSEC2ParamBuilderInternalError description:@"serialize map type value has not been implemented yet" error:error];
- return NO;
-
- } else if ([rulesType isEqualToString:@"timestamp"]) {
- NSDate *timeStampDate;
- //maybe a NSDate type or NSNumber type or NSString type
- if ([value isKindOfClass:[NSString class]]) {
- //try parse the string to NSDate first
- timeStampDate = [NSDate aws_dateFromString:value];
-
- //if failed, then parse it as double value
- if (!timeStampDate) {
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
- }
- } else if ([value isKindOfClass:[NSNumber class]]) {
- //need to convert to NSDate type
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
-
- } else if ([value isKindOfClass:[NSDate class]]) {
- timeStampDate = value;
- }
-
- //generate string presentation of timestamp
- NSString *timestampStr = @"";
- if ([shape[@"timestampFormat"] isEqualToString:@"iso8601"]) {
- timestampStr = [timeStampDate aws_stringValue:AWSDateISO8601DateFormat1];
- } else if ([shape[@"timestampFormat"] isEqualToString:@"unixTimestamp"]) {
- timestampStr = [NSString stringWithFormat:@"%.lf",[timeStampDate timeIntervalSince1970]];
- } else {
- //default timeStamp format
- timestampStr = [timeStampDate aws_stringValue:AWSDateISO8601DateFormat1];
- }
-
- if (!timestampStr) {
- timestampStr = @"";
- }
-
- formattedParams[prefix] = timestampStr;
-
- } else if ([rulesType isEqualToString:@"blob"]) {
-
- //encode NSData to Base64String
- if ([value isKindOfClass:[NSString class]]) {
- value = [value dataUsingEncoding:NSUTF8StringEncoding];
- }
- if ([value isKindOfClass:[NSData class]]) {
- NSString *base64encodedStr = [value base64EncodedStringWithOptions:0];
- formattedParams[prefix] = base64encodedStr?base64encodedStr:@"";
- } else {
- [self failWithCode:AWSEC2ParamBuilderInvalidParameter description:@"'blob' value should be a NSData type." error:error];
- return NO;
- }
-
- } else if ([rulesType isEqualToString:@"boolean"]) {
- formattedParams[prefix] = [value boolValue]?@"true":@"false";
- } else {
- formattedParams[prefix] = value;
- }
-
- return YES;
- }
-
- + (NSString *)queryName:(NSDictionary *)shape withDefaultName:(NSString *)defaultName {
-
- NSString *resultStr = shape[@"queryName"]?shape[@"queryName"]:[self upperCaseFirstChar:shape[@"locationName"]];
-
- return resultStr?resultStr:defaultName;
- }
-
- + (NSString *)upperCaseFirstChar:(NSString *) inputString {
- if ([inputString length] < 1) {
- return nil;
- }
- return [[[inputString substringToIndex:1] uppercaseString] stringByAppendingString: [inputString length]>1 ? [inputString substringFromIndex:1] : @"" ];
- }
-
- @end
-
- @implementation AWSJSONBuilder
-
- + (BOOL)failWithCode:(NSInteger)code description:(NSString *)description error:(NSError *__autoreleasing *)error {
- if (error) {
- *error = [NSError errorWithDomain:AWSJSONBuilderErrorDomain
- code:code
- userInfo:@{NSLocalizedDescriptionKey : description}];
- }
- return NO;
- }
-
- + (NSData *)jsonDataForDictionary:(NSDictionary *)params
- actionName:(NSString *)actionName
- serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule
- error:(NSError *__autoreleasing *)error {
-
- id serializedJsonObject = [self buildJSONDictionary:params actionName:actionName serviceDefinitionRule:serviceDefinitionRule error:error];
-
- if (!serializedJsonObject) {
- serializedJsonObject = @{};
- }
-
- if ([NSJSONSerialization isValidJSONObject:serializedJsonObject] == NO) {
- if ([serializedJsonObject isKindOfClass:[NSData class]]) {
- return serializedJsonObject;
- } else {
- [self failWithCode:AWSJSONBuilderInvalidParameter description:[NSString stringWithFormat:@"serialized object is neither a valid json Object nor NSData object: %@",serializedJsonObject] error:error];
- return nil;
- }
-
- } else {
- NSData *bodyData = [NSJSONSerialization dataWithJSONObject:serializedJsonObject
- options:0
- error:error];
- return bodyData;
- }
-
-
- }
-
- + (NSDictionary *)buildJSONDictionary:(NSDictionary *)params
- actionName:(NSString *)actionName
- serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule
- error:(NSError *__autoreleasing *)error {
-
- if ([params count] == 0) {
- return nil;
- }
-
- NSDictionary *actionRule = [[[serviceDefinitionRule objectForKey:@"operations"] objectForKey:actionName] objectForKey:@"input"];
- NSDictionary *definitionRules = [serviceDefinitionRule objectForKey:@"shapes"];
-
- if (definitionRules == (id)[NSNull null] || [definitionRules count] == 0) {
- AWSDDLogError(@"JSON definition File is empty or can not be found, will return un-serialized dictionary");
- return params;
- }
-
-
-
- if ([actionRule count] == 0) {
- [self failWithCode:AWSJSONBuilderUndefinedActionRule description:@"Invalid argument: actionRule is Empty" error:error];
- return nil;
- }
-
- AWSJSONDictionary *rules = [[AWSJSONDictionary alloc] initWithDictionary:actionRule JSONDefinitionRule:definitionRules];
-
- id resultParams = [self serializeMember:rules value:params isPayloadType:NO error:error];
-
- return resultParams;
-
-
- }
-
- + (NSDictionary *)serializeStructure:(NSDictionary *)structureRules values:(NSDictionary *)values error:(NSError *__autoreleasing *)error {
-
- NSMutableDictionary *data = [NSMutableDictionary new];
-
- for (NSString *key in values) {
- id value = values[key];
-
- AWSJSONDictionary *memberShape = structureRules[@"members"][key];
-
- if (memberShape[@"location"]) {
- //It should be another location rather than body, will be process at different place
- continue;
- }
-
- if (memberShape && value) {
- NSString *name = memberShape[@"locationName"]?memberShape[@"locationName"]:key;
- data[name] = [self serializeMember:memberShape value:value isPayloadType:NO error:error];
- }
-
- }
-
- return data;
- }
-
- + (NSArray *)serializeList:(NSDictionary *)listRules values:(NSArray *)values error:(NSError *__autoreleasing *)error {
- NSMutableArray *dataArray = [NSMutableArray new];
-
- for (id value in values) {
-
- [dataArray addObject:[self serializeMember:listRules[@"member"] value:value isPayloadType:NO error:error]];
-
- }
-
- return dataArray;
- }
-
- + (NSDictionary *)serializeMap:(NSDictionary *)mapRules values:(NSDictionary *)values error:(NSError *__autoreleasing *)error {
-
- NSMutableDictionary *data = [NSMutableDictionary new];
-
- for (NSString *key in values) {
- id value = values[key];
- data[key] = [self serializeMember:mapRules[@"value"] value:value isPayloadType:NO error:error];
- }
-
- return data;
- }
-
- + (id)serializeMember:(NSDictionary *)shape value:(id)value isPayloadType:(BOOL)isPayloadType error:(NSError *__autoreleasing *)error {
- NSString *payloadMemberName = shape[@"payload"];
- if (payloadMemberName) {
- id payload = value[payloadMemberName];
- if (payload) {
- AWSJSONDictionary *structureMembersRule = shape[@"members"]?shape[@"members"]:@{};
- AWSJSONDictionary *payloadMemberRules = structureMembersRule[payloadMemberName];
-
- return [self serializeMember:payloadMemberRules value:payload isPayloadType:YES error:error];
- }
- }
-
- NSString *rulesType = shape[@"type"];
- if ([rulesType isEqualToString:@"structure"]) {
-
- if (![value isKindOfClass:[NSDictionary class]]) {
- if (![value isKindOfClass:[NSNull class]]) {
- [self failWithCode:AWSJSONBuilderInvalidParameter description:[NSString stringWithFormat:@"a structure input should be a dictionary but got:%@",value] error:error];
- }
- return @{};
- } else {
-
- return [self serializeStructure:shape values:value error:error];
- }
-
- } else if ([rulesType isEqualToString:@"list"]) {
-
- if (![value isKindOfClass:[NSArray class]]) {
- if (![value isKindOfClass:[NSNull class]]) {
- [self failWithCode:AWSJSONBuilderInvalidParameter description:[NSString stringWithFormat:@"a list input should be an array but got:%@",value] error:error];
- }
- return @[];
- } else {
- return [self serializeList:shape values:value error:error];
- }
-
-
- } else if ([rulesType isEqualToString:@"map"]) {
-
- if (![value isKindOfClass:[NSDictionary class]]) {
- if (![value isKindOfClass:[NSNull class]]) {
- [self failWithCode:AWSJSONBuilderInvalidParameter description:[NSString stringWithFormat:@"a map input should be a dictionary but got:%@",value] error:error];
- }
- return @{};
- } else {
- return [self serializeMap:shape values:value error:error];
- }
-
- } else if ([rulesType isEqualToString:@"timestamp"]) {
-
- NSDate *timeStampDate;
- //maybe a NSDate type or NSNumber type or NSString type
- if ([value isKindOfClass:[NSString class]]) {
- //try parse the string to NSDate first
- timeStampDate = [NSDate aws_dateFromString:value];
-
- //if failed, then parse it as double value
- if (!timeStampDate) {
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
- }
- } else if ([value isKindOfClass:[NSNumber class]]) {
- //need to convert to NSDate type
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
-
- } else if ([value isKindOfClass:[NSDate class]]) {
- timeStampDate = value;
- }
-
- return [NSNumber numberWithDouble:[timeStampDate timeIntervalSince1970]];
-
-
- } else if ([rulesType isEqualToString:@"blob"]) {
-
- //encode NSData to Base64String
- if ([value isKindOfClass:[NSString class]]) {
- value = [value dataUsingEncoding:NSUTF8StringEncoding];
- }
- if ([value isKindOfClass:[NSData class]]) {
- if (isPayloadType) {
- //Do not base64 encoding if it is payload type
- return value;
- } else {
- NSString *base64encodedStr = [value base64EncodedStringWithOptions:0];
- return base64encodedStr?base64encodedStr:@"";
- }
- } else {
- [self failWithCode:AWSJSONBuilderInvalidParameter description:@"'blob' value should be a NSData type." error:error];
- return @"";
- }
-
- } else {
-
- return value;
-
- }
- }
-
- @end
-
- @implementation AWSJSONParser
-
- + (BOOL)failWithCode:(NSInteger)code description:(NSString *)description error:(NSError *__autoreleasing *)error {
- if (error) {
- *error = [NSError errorWithDomain:AWSJSONParserErrorDomain
- code:code
- userInfo:@{NSLocalizedDescriptionKey : description}];
- }
- return NO;
- }
-
- + (NSDictionary *)dictionaryForJsonData:(NSData *)data
- response:(NSHTTPURLResponse *)response
- actionName:(NSString *)actionName
- serviceDefinitionRule:(NSDictionary *)serviceDefinitionRule
- error:(NSError *__autoreleasing *)error {
- if (!data) {
- return [NSMutableDictionary new];
- }
-
- // Amazon Lambda may return non-array/non-dictionary top level objects.
- // They are valid JSON texts according to RFC 7159 and ECMA 404.
- // (RFC 4627 was replaced with RFC 7159 in March 2014.)
- // You need to pass NSJSONReadingAllowFragments here, otherwise, they may fail.
- id result = [NSJSONSerialization JSONObjectWithData:data
- options:NSJSONReadingAllowFragments
- error:error];
-
- NSDictionary *actionRule = [[[serviceDefinitionRule objectForKey:@"operations"] objectForKey:actionName] objectForKey:@"output"];
- if (actionRule == (id)[NSNull null]) {
- actionRule = @{};
- }
-
- NSDictionary *definitionRules = [serviceDefinitionRule objectForKey:@"shapes"];
- if (definitionRules == (id)[NSNull null]) {
- definitionRules = @{};
- }
- if ([definitionRules count] == 0) {
- AWSDDLogError(@"JSON definition File is empty or can not be found, will return un-serialized dictionary");
- return result;
- }
-
- //if the response is error message, just return
- if (response.statusCode/100 != 2) {
- return result;
- }
-
- AWSJSONDictionary *rules = [[AWSJSONDictionary alloc] initWithDictionary:actionRule JSONDefinitionRule:definitionRules];
-
- //check if has payload tag.
- NSString *isPayloadData = rules[@"payload"];
-
- NSMutableDictionary *parsedData = [NSMutableDictionary new];
-
- if (isPayloadData) {
- NSString *shapeName = [rules[@"members"][isPayloadData] objectForKey:@"shape"];
- //
- // Check if we should apply additional serialization; for streaming
- // types or the 'JsonDocument' shape, no additional serialization will
- // be applied and the data will be returned as-is in the response.
- // The 'JsonDocument' shape is used by the AWSIoT service.
- //
- if ((rules[@"members"][isPayloadData][@"streaming"]) || ([shapeName isEqual:@"JsonDocument"]) || ([shapeName isEqual:@"BlobStream"])) {
- parsedData[isPayloadData] = data;
- if (error) *error = nil;
- return parsedData;
- }
-
- rules = rules[isPayloadData][@"members"];
- parsedData[isPayloadData] = [self serializeMember:rules value:result target:nil error:error];
-
- } else {
- parsedData = [self serializeMember:rules value:result target:nil error:error];
- }
-
-
- return parsedData;
- }
-
- + (NSString *)findMemberName:(NSString*)locationName structureRules:(NSDictionary *)structureRules {
-
- for (NSString *aMember in structureRules[@"members"]) {
- NSDictionary *memberShape = structureRules[@"members"][aMember];
-
- if ([memberShape[@"locationName"] isEqualToString:locationName]) {
- return aMember;
- }
- }
-
- return locationName;
- }
-
- + (id)serializeStructure:(NSDictionary *)structureRules values:(NSDictionary *)values target:(id)target error:(NSError *__autoreleasing *)error{
- if (!target) {
- target = [NSMutableDictionary new];
- }
-
- for (NSString *serialized_name in values) {
- id value = values[serialized_name];
-
- NSString *memberName = [self findMemberName:serialized_name structureRules:structureRules];
-
- AWSJSONDictionary *memberShape = structureRules[@"members"][memberName];
- if (memberShape && value) {
- // NSString *name = memberShape[@"locationName"]?memberShape[@"locationName"]:serialized_name;
- target[memberName] = [self serializeMember:memberShape value:value target:nil error:(NSError *__autoreleasing *)error];
- }
-
- }
- return target;
-
- }
-
- + (NSMutableArray *)serializeList:(NSDictionary *)listRules values:(NSDictionary *)values target:(id)target error:(NSError *__autoreleasing *)error{
- if (!target) {
- target = [NSMutableArray new];
- }
-
- for (NSString *value in values) {
- [target addObject:[self serializeMember:listRules[@"member"] value:value target:nil error:(NSError *__autoreleasing *)error]];
- }
-
- return target;
- }
-
- + (NSMutableDictionary *) serializeMap:(NSDictionary *)mapRules values:(NSDictionary *)values target:(id)target error:(NSError *__autoreleasing *)error{
- if (!target) {
- target = [NSMutableDictionary new];
- }
-
- for (NSString *key in values) {
- id value = values[key];
-
- target[key] = [self serializeMember:mapRules[@"value"] value:value target:nil error:(NSError *__autoreleasing *)error];
- }
-
- return target;
- }
-
- + (id)serializeMember:(NSDictionary *)shape value:(id)value target:(id)target error:(NSError *__autoreleasing *)error{
- if (!value) {
- return nil;
- }
-
- NSString *rulesType = shape[@"type"];
- if ([rulesType isEqualToString:@"structure"]) {
-
- if (![value isKindOfClass:[NSDictionary class]]) {
- if (![value isKindOfClass:[NSNull class]]) {
- [self failWithCode:AWSJSONParserInvalidParameter description:[NSString stringWithFormat:@"a structure input should be a dictionary but got:%@",value] error:error];
- }
- return @{};
- } else {
-
- return [self serializeStructure:shape values:value target:target error:error];
- }
-
- } else if ([rulesType isEqualToString:@"list"]) {
-
- if (![value isKindOfClass:[NSArray class]]) {
- if (![value isKindOfClass:[NSNull class]]) {
- [self failWithCode:AWSJSONParserInvalidParameter description:[NSString stringWithFormat:@"a list input should be an array but got:%@",value] error:error];
- }
- return @[];
- } else {
- return [self serializeList:shape values:value target:target error:error];
- }
-
-
- } else if ([rulesType isEqualToString:@"map"]) {
-
- if (![value isKindOfClass:[NSDictionary class]]) {
- if (![value isKindOfClass:[NSNull class]]) {
- [self failWithCode:AWSJSONParserInvalidParameter description:[NSString stringWithFormat:@"a map input should be a dictionary but got:%@",value] error:error];
- }
- return @{};
- } else {
- return [self serializeMap:shape values:value target:target error:error];
- }
-
- } else if ([rulesType isEqualToString:@"timestamp"]) {
-
- NSDate *timeStampDate;
- //maybe a NSDate type or NSNumber type or NSString type
- if ([value isKindOfClass:[NSString class]]) {
- //try parse the string to NSDate first
- timeStampDate = [NSDate aws_dateFromString:value];
-
- //if failed, then parse it as double value
- if (!timeStampDate) {
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
- }
- } else if ([value isKindOfClass:[NSNumber class]]) {
- //need to convert to NSDate type
- timeStampDate = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
-
- } else if ([value isKindOfClass:[NSDate class]]) {
- timeStampDate = value;
- }
-
- return [NSNumber numberWithDouble:[timeStampDate timeIntervalSince1970]];
-
-
- } else if ([rulesType isEqualToString:@"blob"]) {
-
- //decode Base64Str to NSData
- if ([value isKindOfClass:[NSString class]]) {
- NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:value options:0];
- //return origin string value if can not be encoded.
- return decodedData?decodedData:value;
- } else {
- [self failWithCode:AWSJSONParserInvalidParameter description:@"blob value should be NSString type." error:error];
- return [NSData new];
- }
- } else {
- return value;
- }
- }
-
- @end
|