I wouldn't recommend using JsonNodeunless your goal is to read/write/manipulate JSON data.
If you just want a mapping of keys to values, there's usually a better way which will be more maintainable and also more performant:
Are the keys ordinal (range of int, enum etc.) and the values bool? ->Use a set.
Are the keys ordinal and the values all the same type? ->Use an array.
Are the keys ordinal and the values one of a few limited types? ->Use an array of object variants.
Are the keys string, float, or some other type, and the values bool? ->Use a HashSet.
Are the keys string, float, etc. and the values all the same type? ->Use a Table.
Are the keys string, float, etc. and the values one of a few limited types? ->Use a Table of object variants.
Are the keys integers from a huge range (e.g. 64-bit integers) and the values bool? ->Use an IntSet.
Are the keys strings and the values also strings? ->Use a StringTable.
Are all possible keys and values actually known in advance, and you don't really need to loop over them, but some of them may not always be present? Use fields with Optiontypes.
Are the keys and values ~complicated~? Think about it some more, there's a fair chance you can still model your data properly using the above.
You mention you want to add/set/get fields dynamically. Are there some fields that are always present, and some that you don't know until runtime? Are the values always either numeric or text? You could model it like this:
typeAttributeKind=enumakFloatakStringAttribute=objectcasekindofakFloat:floatVal:floatofakString:strVal:stringFancyObject=objectname:string# mandatory fieldsx,y:int# ..attrs:Table[string,Attribute]# extra fields only known at runtime
I'd say use tables, arrays and objects by default. These are fast types, and you don't have to call a functions (e.g. getStr()) to get values the way you have to with JsonNode. Use JsonNode when you have to work with data that is serialized to JSON at some point.