5
\$\begingroup\$

Quick disclaimer. I've programmed Java for years but this is the first Objective C I've ever written.

I've written some code which almost unfortunately works but frankly hurts my eyes with the number of lines of code and quality. Basically looking for the right way to convert the original string:

<TUHandle 0x280479f50 type=PhoneNumber, value=07700000000, normalizedValue=(null), isoCountryCode=(null)>

Into JSON (ignoring the TUHandle 0x280479f50) part which I don't need:

{"value": "07700000000",
"normalizedValue": "(null)",
"type": "PhoneNumber",
"isoCountryCode": "(null)"}

Line breaks and indents are NOT important, only that this is valid JSON

        //Format of string
        //<TUHandle 0x280479f50 type=PhoneNumber, value=07700000000, normalizedValue=(null), isoCountryCode=(null)>
        NSString *original = [NSString stringWithFormat:@"%@", hand];

        //Trim off unused stuff
        NSRange startKeyValues = [original rangeOfString:@"type="];
        NSRange endKeyValues = [original rangeOfString:@">"];
        NSRange rangeOfString = NSMakeRange(startKeyValues.location, endKeyValues.location - startKeyValues.location);
        NSString *keysValues = [original substringWithRange:rangeOfString];

        //Create a dictionary to hold the key values
        NSMutableDictionary *dict = [[NSMutableDictionary alloc]initWithCapacity:10];

        //Split the keysValuesstring
        NSArray *items = [keysValues componentsSeparatedByString:@","];

        for (NSString* o in items)
        {
            //Create key value pairs
            NSArray *item = [o componentsSeparatedByString:@"="];
            NSString *key=[item objectAtIndex:0]; 
            NSString *value=[item objectAtIndex:1];
            [dict setObject:value forKey:key];
        }

        [dict setObject:currentUUID forKey:@"uid"];

        //Convert to Json Object
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];

Any tips which would make this look a hell of a lot less clunky?

\$\endgroup\$
4
  • \$\begingroup\$ A quick question: Where does the string come from, or how is it generated? I have the feeling that there might be a better approach by generating the JSON from some original object and not from the string description of that object. \$\endgroup\$
    – Martin R
    Commented Sep 26, 2020 at 18:50
  • \$\begingroup\$ Thanks a @MartinR it's a hook in a Theos Logos thing I'm working on. So using %hook TUProxyCall -(id)handle { TUHandle *hand = %orig; NSString *original = [NSString stringWithFormat:@"%@", hand]; ....} Thanks in advance \$\endgroup\$ Commented Sep 26, 2020 at 19:15
  • \$\begingroup\$ I suggest that you add more information to your question, so that it becomes clear in which context the above code is used, and what you are ultimately trying to achieve. – I have no experience with Theos Logos, so I might be completely wrong, but at present this looks a bit like an XY-problem to me: You want to create JSON from some object, but what you ask is how to create JSON from a string description of that object. \$\endgroup\$
    – Martin R
    Commented Sep 26, 2020 at 19:30
  • \$\begingroup\$ Thanks but if I wanted an answer on Theos Logos I would have asked on a different forum. This question is specifically about String manipulation and formatting Key Value pairs as JSON. In terms of what I am trying to achieve, I already achieved it but I would like to do it with cleaner code. \$\endgroup\$ Commented Sep 26, 2020 at 21:24

1 Answer 1

1
\$\begingroup\$

In your question, you don't make it clear what needs to be dynamic and what can be static. If the only string you ever have to parse is the one you showed, then don't bother parsing it, just make the json string and use:

NSData *jsonData = [@"{\"normalizedValue\":\"(null)\",\"value\":\"07700000000\",\"isoCountryCode\":\"(null)\",\"type\":\"PhoneNumber\",\"uid\":\"uuid\"}" dataUsingEncoding:NSUTF8StringEncoding];

If you know all the keys will be the same and just the data will be different, then I would go with something like:

NSString *valueFor(NSString *key, NSString *original) {
    unsigned long startOf = [original rangeOfString:key].location + key.length;
    unsigned long endOfComma = [original rangeOfString:@"," options:NSLiteralSearch range:NSMakeRange(startOf, original.length - startOf)].location;
    return [
            [original substringWithRange:NSMakeRange(startOf, (endOfComma == NSNotFound ? original.length : endOfComma) - startOf)]
            stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@">"]
            ];
}

NSData *example(NSString *original, NSString *currentUUID) {
    NSString *type = valueFor(@"type=", original);
    NSString *value = valueFor(@"value=", original);
    NSString *normalizedValue = valueFor(@"normalizedValue=", original);
    NSString *isoCountryCode = valueFor(@"isoCountryCode=", original);
    NSDictionary *dict = @{
        @"value": value,
        @"normalizedValue": normalizedValue,
        @"type": type,
        @"isoCountryCode": isoCountryCode,
        @"uid": currentUUID
    };

    return [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
}

The above is much cleaner and easier to read than what you have currently.

If even the keys have to be dynamic, then the code you have is about as good as you can get.

I ended up with this:

NSData* example(NSString* original, NSString *currentUUID) {
    NSArray<NSString*>* elements =
    [[[original stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]
      componentsSeparatedByString:@" "]
     filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id  _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
        return [evaluatedObject containsString:@"="];
    }]];

    NSMutableArray* values = [NSMutableArray new];
    for (NSString* each in elements) {
        NSArray<NSString*>* keyValue = [[each stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]]
                                        componentsSeparatedByString:@"="];
        NSString* value = [NSString stringWithFormat:@"\"%@\": \"%@\"", keyValue[0], keyValue[1]];
        [values addObject:value];
    };
    [values addObject: [NSString stringWithFormat:@"\"uid\": \"%@\"", currentUUID]];

    NSData* jsonData = [[NSString stringWithFormat:@"{%@}", [values componentsJoinedByString:@", "]] dataUsingEncoding:NSUTF8StringEncoding];
    return jsonData;
}

Which is maybe a little more generic than the code you have, but I don't think it's any more readable.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.