2

I am developing an API that has one call that accepts a big JSON object.

Based on this object, there are 10 possible parsing scenarios, i.e. if field xxxx.xxx is present, go with scenario 5.

We currently have around 10 if statements in a controller function of over 200 lines, which decides which scenario to choose. The cyclomatic complexity is over 5000 and the nPath complexity is over 10 million

As this is not scalable, I finally made time to refactor this and make it more loosely coupled

What would be the best possible design pattern for this case?

I was thinking of having multiple parser classes that I loop through and each of them has a function called canHandle($jsonObject), and if certain fields are present, canHandle will return true and that class can then do it's logic.

Is this similar to the strategy pattern?


More background:

It is a booking tool for point to point travel.

So say I want to travel from an airport to a trainstation, the JSON would have the following fields:

route.pickuppoint.airport.iata
route.dropoffpoint.trainstation.id

There are multiple entities that can exist under pickuppoint and dropoffpoint(trainstations/airports/addresses/flight numbers) and they have to be parsed differently into our database

So if anyone calls the API with an airport in the pickup, I need to parse the airport and translate it to an address using some external api's and put it in the DB, the same goes for the other entities

Code example:

if (isset($data['Address']['Street'])) {
        // Parse address
    } elseif (isset($data['Location']['Latitude'])) {
        // Parse Location
    } elseif (isset($data['Airport']['Id'])) {
        // Parse airport
    } elseif (isset($data['Flight']['FlightNumber'])) {
        // Resolve flight number and parse
    } elseif (isset($data['Trainstation']['id'])) {
        // Resolve train station
    }

As you can imagine, if we were to add more options, this code would only get uglier

Tulains Córdova
  • 39,570
  • 13
  • 100
  • 156
Mazzy
  • 171

2 Answers2

1

Can every piece of ode that creates the JSON in question add a uniform, unambiguous marker for the type of the information it produces?

Consider:

kind = $data['kind']  // always present.
if (kind == POSTAL_ADDRESS) {
  ...
} 
else if (kind == COORDINATES) {
  ...
}
// etc
9000
  • 24,342
0

Here's a general approach: write each parser as a function into some appropriate domain that can fail. I would tend to use an Optional return value to signal failure, but it may be more convenient to use exceptions depending on the language / situation. You would then have

parser1 : JSON -> Optional[Type1]
parser2 : JSON -> Optional[Type2]
...
parserA : JSON -> Optional[TypeA]

And then write

parser : JSON -> Optional[Type1 | Type2 | ... | TypeA]
parser json = parser1(json).or(parser2(json))...or(parserA(json))

The reason I think this is preferable to using canHandle functions is that these sorts of guards tend to introduce duplication between the check and execution (e.g. your canHandle has to check the same paths exist as what your parser reads), introduce temporal coupling in your consumers (your consumer must remember to always check canHandle before parsing) and often lead to wasted cycles (canHandle doing some preliminary parsing work on its own).

If the question is what pattern fits here, then I agree that a strategy pattern is appropriate.

walpen
  • 3,241