aboutsummaryrefslogtreecommitdiff
path: root/Foundation/GTMNSScanner+JSON.m
blob: 513ce30bef71e47fec57ec56acfe9e7adfae4028 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//
//  GTMNSScanner+JSON.m
//
//  Copyright 2009 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations under
//  the License.
//

#import "GTMDefines.h"
#import "GTMNSScanner+JSON.h"

@implementation NSScanner (GTMNSScannerJSONAdditions)

- (BOOL)gtm_scanJSONString:(NSString **)jsonString
                 startChar:(unichar)startChar
                   endChar:(unichar)endChar {
  BOOL isGood = NO;
  NSRange jsonRange = { NSNotFound, 0 };
  NSString *scanString = [self string];
  NSUInteger startLocation = [self scanLocation];
  NSUInteger length = [scanString length];
  NSUInteger blockOpen = 0;
  NSCharacterSet *charsToSkip = [self charactersToBeSkipped];
  BOOL inQuoteMode = NO;
  NSUInteger i;
  for (i = startLocation; i < length; ++i) {
    unichar jsonChar = [scanString characterAtIndex:i];
    if (jsonChar == startChar && !inQuoteMode) {
      if (blockOpen == 0) {
        jsonRange.location = i;
      }
      blockOpen += 1;
    } else if (blockOpen == 0) {
      // If we haven't opened our block skip over any characters in
      // charsToSkip.
      if (![charsToSkip characterIsMember:jsonChar]) {
        break;
      }
    } else if (jsonChar == endChar && !inQuoteMode) {
      blockOpen -= 1;
      if (blockOpen == 0) {
        i += 1; // Move onto next character
        jsonRange.length = i - jsonRange.location;
        break;
      }
    } else {
      if (jsonChar == '"') {
        inQuoteMode = !inQuoteMode;
      } else if (inQuoteMode && jsonChar == '\\') {
        // Skip the escaped character if it isn't the last one
        if (i < length - 1) ++i;
      }
    }
  }
  [self setScanLocation:i];
  if (blockOpen == 0 && jsonRange.location != NSNotFound) {
    isGood = YES;
    if (jsonString) {
      *jsonString = [scanString substringWithRange:jsonRange];
    }
  }
  return isGood;
}

- (BOOL)gtm_scanJSONObjectString:(NSString **)jsonString {
  return [self gtm_scanJSONString:jsonString startChar:'{' endChar:'}'];
}

- (BOOL)gtm_scanJSONArrayString:(NSString**)jsonString {
  return [self gtm_scanJSONString:jsonString startChar:'[' endChar:']'];
}

@end