TaJson lets you use a ViewModel as a declarative template to create JSON from model objects or import JSON into them; use it when you need a mapping and transformation layer between an external JSON contract and your model.
TaJson is a declarative approach to receive, send, transform, and store JSON messages, with setup and maintenance environment done in minutes.
Learn more about TaJson's background and approach: You can find the original introduction to TaJson here: https://blog.mdriven.net/introducing-mdriven-tajson/
How TaJson works
TaJson uses the columns and nestings in a ViewModel as the shape of the JSON document.
- A ViewModel column becomes a JSON property.
- A ViewModel multi-nesting becomes a JSON array.
- Column and nesting names provide the JSON names; expressions determine the model values that are read or written.
- You can create a dedicated, lightweight ViewModel containing only the fields that belong in an import or export contract.
For example, a template with columns named author_name and title can map an external JSON property named author_name to a differently named model attribute through the column expression. See MergeTaJson for a mapping example.
TaJson differs from JsonToObject. JsonToObject imports matching JSON directly into newly created model objects. TaJson always uses a ViewModel as its template, so you can choose fields, rename fields through ViewModel columns, and control the hierarchy used for both output and input.
Choose the operation
| Goal | Operation | Result |
|---|---|---|
| Create JSON from an existing object hierarchy | AsTaJson | Returns JSON shaped by the ViewModel. |
| Update fields represented in incoming JSON | MergeTaJson | Imports JSON using the ViewModel mapping. The operation returns a log string. |
| Make the object hierarchy match incoming JSON | ApplyTaJson | Imports JSON using the ViewModel mapping and removes objects missing from JSON when processing multi-associations. The operation returns a log string. |
Create JSON from objects
Use AsTaJson on the object you want to export and provide the ViewModel template. The ViewModel defines which attributes and associations are included.
For example, an object can be serialized with a template ViewModel using an expression in this form:
vCurrent_Article.AsTaJson(Articles1.ViewModels.ArticlesJsonTemplate, false)
Create the template ViewModel with the columns and multi-nestings required by the receiving JSON contract. Keep the template separate from a user-facing ViewModel when the API contract needs a different field set or names.
Return custom JSON from a REST endpoint
When a ViewModel is exposed as a REST endpoint, MDriven normally serializes the ViewModel using its default format. To return TaJson output as the complete response body:
- Add a ViewModel column named exactly
RawJson. - Set the column expression to an
AsTaJsoncall. - MDriven uses the returned string as the entire HTTP response body.
See AsTaJson for this REST response pattern.
Control JSON output with tagged values
Set these tagged values to true on a ViewModel multi-nesting when the standard array-of-objects output does not match the external contract.
| Tagged value | Use it when | Result |
|---|---|---|
TaJsonTreatListAsDynamicProperties
|
Property names are known only at runtime. | TaJson reads Name and Value properties from each object in the nesting and emits them as properties on the containing JSON object.
|
TaJsonTreatListAsValues
|
The contract requires an array of scalar values rather than an array of objects. | TaJson emits the first column of each nesting row as an array value. |
Dynamic properties
Use TaJsonTreatListAsDynamicProperties for a nesting whose items have string columns named Name and Value. This lets your model represent arbitrary JSON properties that were not known when you designed the ViewModel.
For example, a nesting containing these rows:
| Name | Value |
|---|---|
priority
|
high
|
region
|
north
|
can produce:
{
"priority": "high",
"region": "north"
}
The same setting also supports import. When an incoming JSON property does not match a ViewModel column at that level, TaJson checks for a multi-nesting tagged with TaJsonTreatListAsDynamicProperties=true. If it finds one, it imports unmatched properties as Name/Value pairs into that nesting.
This is useful when an external service sends extension properties that can vary between messages.
Arrays of values
Without TaJsonTreatListAsValues, a nesting with columns Code and Description produces an array of JSON objects:
{
"someList": [
{ "Code": "A", "Description": "First" },
{ "Code": "B", "Description": "Second" }
]
}
With TaJsonTreatListAsValues=true, TaJson uses the first column only. If Code is the first column, the output becomes:
{
"someList": ["A", "B"]
}
Place the intended value column first in the nesting. Other columns in that nesting are not represented by this array-of-values output.
Insert preformatted JSON
Set the RawJSon tagged value to true when an AsTaJson column contains JSON text that must be inserted as JSON rather than quoted as a string.
For example, if a column evaluates to:
{"enabled":true}
normal string serialization would produce a quoted value. With RawJSon=true, the text is inserted as a JSON object. The value must be valid JSON.
Import JSON into objects
Use MergeTaJson or ApplyTaJson with a target root object and a ViewModel that describes the incoming hierarchy. Give the root object to the ViewModel; TaJson then updates the hierarchy below that root by matching JSON attributes and associations to ViewModel columns and nestings.
- Create a dedicated import ViewModel or confirm that the existing ViewModel contains the required columns and nestings.
- Ensure the root object is available to that ViewModel.
- Call
MergeTaJsonfor a merge-style update, orApplyTaJsonwhen missing items in multi-associations must be removed. - Store or inspect the returned log string when diagnosing an import.
Object creation, lookup, and cleanup hooks
TaJson recognizes these ViewModel action and variable conventions during import:
| Convention | Purpose |
|---|---|
<ViewModelColumn>_AddNew action
|
Creates an object needed for the ViewModel association named <ViewModelColumn>. The action must return the created object and must add that object to the association. If the action is absent, TaJson uses type information to create an object.
|
Delete action in a nesting
|
Used when TaJson needs to delete an object that is absent from the incoming data and merge mode is not in effect. |
vImportKey:string variable
|
Updated before object creation. Use it in an _AddNew action to find an existing object instead of creating a duplicate.
|
CleanUpAction action on the root ViewModel class
|
Called after the import finishes. Prepare it as a column for action in the ViewModel Editor. |
For example, if the JSON contains an Invoices array, an Invoices_AddNew action can use vImportKey to locate the invoice identified by the incoming key, return it, and ensure it belongs to the Invoices association.
Scale imports for large data sets
The default import behavior loads objects in many-associations so TaJson can match incoming keys against existing objects. That can consume excessive time and memory when an association contains millions of objects.
To limit the objects loaded for a many-association:
- Add an action named
<ViewModelColumn>_Lookup, where<ViewModelColumn>is the association column in the ViewModel. - Add a variable that holds the collection of objects that may be updated, for example
vclient_invoices. - Use that variable as the expression for the many-association.
- In the lookup action, use
vImportKey. TaJson supplies a comma-separated list of keys from the JSON. - Load only the objects for those keys and add them to the collection variable. The current implementation example uses OCL PS/ExecutePS for this lookup.
- When the lookup action finishes, TaJson continues with its normal import processing.
With ApplyTaJson, take special care: ApplyTaJson removes existing objects that are not found in the JSON. Any objects that must be retained or evaluated for deletion need to be present in the collection you supply.
Split very large, deep documents
A deeply nested document can require TaJson to resolve and set a very large number of single links and leaf objects in one operation. For example, 1,000 Details, each with 1,000 Deep1 objects, each with 1,000 Deep2 objects, represents up to 1,000,000,000 objects to process.
Split this work into smaller passes by adding a ViewModel column named exactly RawJSon (case-sensitive) in the nesting where processing should stop.
When MergeTaJson or ApplyTaJson finds that column, it stores the JSON text for the current level and below in the column as a string. You can then process each stored fragment later with another ViewModel and another import call.
For example, first import the top-level Details items and store each item's deep content in RawJSon. Then process a manageable group of Detail objects at a time, importing each stored JSON fragment with a ViewModel that starts at Deep1. This approach is suitable for background processing such as MDrivenServer server-side jobs, where each fragment can be handled as a separate unit of work.
Consider this JSON structure as an example: Consider this JSON:
{
"SomeString": "Hello",
"Details": [
{
"Attribute1": "1111",
"Attribute2": "222222",
"Deep1": [
{
"asString": "Deep1",
"Attribute1": "1111",
"Deep2": [
{
"asString": "Deep2",
"Attribute1": "222"
}
]
}
]
}
]
}
Importing with a partial ViewModel produces this result: If we now import with ViewOneThing_SemiDeep, we get a result that looks like this (when we read it back with AsTajson):
{
"SomeString": "Helloxxx",
"Details": [
{
"Attribute1": "1111",
"Attribute2": "222222",
"RawJSon": "{\r\n \"Attribute1\": \"1111\",\r\n \"Attribute2\": \"222222\",\r\n \"Deep1\": [\r\n {\r\n \"asString\": \"Deep1\",\r\n \"Attribute1\": \"1111\",\r\n \"Deep2\": [\r\n {\r\n \"asString\": \"Deep2\",\r\n \"Attribute1\": \"222\"\r\n }\r\n ]\r\n }\r\n ]\r\n}"
}
]
}
Troubleshooting and gotchas
Check ReadOnly on import columns
When you add attributes to multi-nestings, the framework sets ReadOnly to true. That default is often appropriate for UI grid cells, but in TaJson it means the field is read-only during import as well.
Before testing an import, review the ReadOnly setting on every column that incoming JSON must update. Set it according to the import behavior you need.
Use valid JSON for raw insertion
The RawJSon=true tagged value bypasses normal string quoting. Invalid JSON in the value produces invalid output, so validate the generated fragment before using it in an external contract.
Do not use ApplyTaJson for an unintended partial list update
ApplyTaJson removes missing objects in multi-associations. If the source payload contains only a subset of the association, use MergeTaJson or ensure the ApplyTaJson input represents the intended complete set.
Create a model template from a sample payload
If you have an existing JSON or XML example, you can generate a starting model section for an import target. Right-click the class that should be the root and choose Add attributes, assoc. and classes from clipboard json or xml. Review and adapt the generated classes and then create the ViewModel template used by TaJson. For the full procedure, see Documentation:Using JSON or XML as class template.
See also
- Documentation:OCLOperators AsTaJson
- Documentation:OCLOperators MergeTaJson
- Documentation:OCLOperators ApplyTaJson
- Documentation:JsonToObject vs Tajson
- Documentation:Using JSON or XML as class template
Tagged values to change the output
TaJsonTreatListAsDynamicProperties. When this is true, we read the nesting in the ViewModel object and look for Name and Value properties. We then use the result as properties on the resulting JSON object. This enables you to create JSON with the definition given in runtime. When properties in Json are not matched with ViewModel columns on the same level, we check for a ViewModel multilink nesting that has the tagged value TaJsonTreatListAsDynamicProperties==true. If found, we turn all the unmatched JSON attributes into Name+Value pairs and send them to the ViewModel multilink. If this nesting has ViewModel columns Name and Value (strings), you get the names and values for properties you did not know in design time.

someList:{ {value1,value2}} instead of someList:{ {col1:value1,col2:othervalue1},{col1:value2,col2:othervalue2}}

RawJSon=true
Examples
Common scenarios and solutions when working with TaJson. How can I create an array of named objects that are empty? For example in this example when tooling an AI request

This is the model section, a AITool class that is a multi-link from an owning class.
Visual reference for the model section described above.
Improving performance if updating an existing large dataset
To optimize performance, use a variable to pre-load the target objects. Looking at the image below, this is the explanation:
- A variable holding a collection of the objects you want to update. Here called vclient_invoices.
- Use that variable as the expression of the many associations.
- Tajson will call the lookup action expecting it to fill the many associations. There are many ways to do that, but here the expression will use oclPS to get client invoices.
- When Tajson calls the lookup action, vImportKey will contain a comma-separated list of the keys from the JSON.
- In the example, the oclPS is called for each key, and the resulting object is added to the list.
- vClientID is used to pass the key value to the oclPS expression. See ExecutePS for more information.
- When the action is done, Tajson proceeds as normal.
Please note that using this feature with ApplyTaJson needs special consideration. ApplyTaJson removes objects from the existing database not found in the JSON. To use this feature, the objects need to be present in the list you provide.

Example:
An example of handling deeply nested JSON structures. We want to read this into a model like this:

For this, we can use ApplyTaJSon with a ViewModel that looks like this:

If we sometimes get a JSON data structure with 1000 Detail objects and each Detail object has 1000 Deep1 objects and each Deep1 has 1000 Deep2 objects, this would force ApplyTaJson to manage 1000*1000*1000 objects at once and that will not work.
The RawJSon attribute helps split and manage large imports. To handle this, we make use of the RawJSon attribute in a new ViewModel like this:

