Design – HashMap versus Object ? Which one is better to finally generate JSON output with KEY and VALUE

coding-standardscoding-styledesignjsonobject

I do have this use case.

  1. callers(APIs in same microservice or different microservices) wants to report different KEYS and VALUES as an audit as part of their methods.
  2. There is a high possibilities that some of KEYS will be very common that majority of callers can report.
  3. consistency is MUST when using KEY NAME from multiple callers to report same KEY. Example: if all wants to report consumer ID, then all callers need to use key "consumerId" only to bring consistency across different callers.
  4. Can have some reusable enum class to hold all common keys like above so that every callers can use. Callers can still have their own custom KEYS which they want to report.
  5. All callers should not do a duplicate jobs like if caller1, caller2 wants to report consumerID (common KEY), then I dont want both to define class with consumerID as property and then convert that into JSON.

I thought to use below kind of implementation.

Enum to hold common KEYS

public enum EnumKeys implements Key {
    CONSUMERID("consumerId")
    ...
    ...
    ...
}

Common class each caller can use to report KEY AND VALUE

@JsonPropertyOrder(alphabetic=true)
public class ReportData {

    private Map<String, Object> properties = new HashMap<>();

    @JsonAnyGetter
    public Map<String, Object> getProperties() {
        return properties;
    }

    public void setProperties(Map<String, Object> properties) {
        this.properties = properties;
    }

    public ReportData add(Key key, Object value){
        properties.put(key.getKeyName(), value);
        return this;
    }
}

Caller 1 Reporting consumerID

ReportData reportData = new ReportData();
reportData.add(EnumKeys.CONSUMERID,"123");

Caller 2 Reporting consumerID

ReportData reportData = new ReportData(); 
reportData.add(EnumKeys.CONSUMERID,"435");

Caller 3 Reporting XYZ key which is not common

//First create a custom KEY
public enum CustomEnum implements Key{

    XYZ("xyz");
}

ReportData reportData = new ReportData();
reportData.add(CustomEnum.XYZ,"custom value");

Finally framework will convert each property key pair to JSON

ObjectMapper mapper = new ObjectMapper().writeValueAsString(reportData);

Output

  1. Not all callers needs to create holder class (equivalent to ReportData) to hold properties.
  2. Using instance of ReportData, they can bring consistency on using same KEY name for KEY
  3. Callers can extend EnumKeys (master list of KEYs) and define their own keys and still can use same way of reporting.

Questions

  1. Is there anything bad about this way of usage?
  2. Anything bad to instantiate object (ReportData) out of HashMap/EnumMap?
  3. Is object (with instance members) better to generate JSON or HASHMAP with more constrained KEY NAME is better? Final destination is JSON.
  4. Any easy way to use existing class (with n of properties) and use with ReportData class.

Any input is appreciated.

Best Answer

Is there anything bad about this way of usage?

Yes.

  1. You are exposing internal data-structures in ReportData.getProperties(). Return either a copy, or an object that references that data-structure and enforces appropriate interaction.

  2. You are forcing all clients to define an Enumeration to hold their "key" values, and enumerations are compiled. Some clients will need to be able to express "Custom" keys that are dynamic and only known at run time. Unless of course Key can be freely constructed?

  3. I do not see how you are handling nested objects/arrays. Are you only supporting a Flat JSON object? JSON Values can contain nested objects and arrays.

Point 2 and 3 might not apply to your specific use case.

Anything bad to instantiate object (ReportData) out of HashMap/EnumMap?

Nothing bad about the idea. That is the whole purpose of a constructor.

However it appears that you are creating the object first, then setting its contents. This makes it possible for the ReportData object to be in an invalid state should something happen that prevents the contents from being set.

Is object (with instance members) better to generate JSON or HASHMAP with more constrained KEY NAME is better? Final destination is JSON.

That depends entirely on context.

If the serialiser can correctly encode an object into JSON, and the object is guaranteed to be a DAG (Directed Acyclic Graph), and the object and all of the objects it returns are guaranteed to not change during serialisation. Then you can get away with a direct object to JSON conversion.

If for some reason:

  • The keys need to be altered
  • The object does not represent a DAG
  • The object cannot be trusted to not change
  • Other pre-processing is required
  • An intermediate representation is needed for other actions

Then you probably do need to convert it to an in memory representation like ReportData. Although I would not enforce domain logic on this representation it should be a direct representation of the textual JSON.

Any easy way to use existing class (with n of properties) and use with ReportData class.

Easy is a tricky word. What is easy for a human, is generally painful for a machine and vice-versa.

I think your question is specifically: Is there a way to directly map an instance of an arbitrary class into a ReportData object?

The short answer is probably yes. The language appears to be Java. You can use reflection to explore a class definition. Using a couple of mapping techniques you could extract the class properties, and even instantiate an instance of that class based on a set of key-value-pairs. You may wish to use annotations to help control the mapping.

However this is probably over-designing the problem (and unless this is for practice). You probably do not need the intermediate container. There are numerous libraries out there for serialising JSON to and from objects in most languages. Leverage them, and supply just the glue needed to cast the JSON definition into a class instance, and vice-versa.