Hone logo
Hone
Problems

Go Response Transformation API

You are tasked with building a Go service that can transform API responses based on a given set of transformation rules. This service will be crucial for adapting data structures between different internal services or for preparing data for external consumers.

Problem Description

Your goal is to create a Go program that takes an input JSON object and a set of transformation rules, and outputs a new JSON object that reflects these transformations. The transformations should support renaming fields, embedding existing fields into new structures, and removing fields.

Key Requirements:

  1. Input: The program will receive two inputs:
    • An interface{} representing the raw JSON data (decoded into a Go map[string]interface{}).
    • A map[string]interface{} representing the transformation rules.
  2. Transformations: The rules will specify how to transform the input data. Supported transformations include:
    • rename: Change the key of an existing field.
    • embed: Create a new nested structure and place existing fields within it.
    • remove: Delete a field from the output.
  3. Output: The program should return a new map[string]interface{} representing the transformed JSON data.
  4. Rule Structure: The transformation rules will be a map where keys are the target field names in the output, and values are objects describing the transformation.
    • For rename: {"<new_name>": {"type": "rename", "from": "<original_name>"}}
    • For embed: {"<new_parent_name>": {"type": "embed", "fields": ["<field1>", "<field2>"], "parent_field": "<parent_field_to_embed_in>"}}. If parent_field is not specified, a new parent field will be created.
    • For remove: {"<field_to_remove>": {"type": "remove"}} (Note: The key here is the field to be removed from the input, not a target field in the output. This is a simplification for this challenge.)

Expected Behavior:

  • Fields not mentioned in the rules should be copied directly to the output.
  • If a rename operation targets a field that doesn't exist in the input, it should be ignored.
  • If an embed operation targets fields that don't exist in the input, they should be omitted from the embedded structure.
  • The remove operation should ensure the specified field is not present in the final output.
  • Nested JSON structures should be handled recursively.

Edge Cases to Consider:

  • Empty input JSON.
  • Empty transformation rules.
  • Transformation rules that conflict (e.g., renaming a field to a name already used by another rule). The order of rule application is not explicitly defined, so consider a consistent approach (e.g., process renames first, then embeds). For this challenge, assume the rules are processed in the order they appear in the map (Go map iteration order is not guaranteed, so a structured approach is better, but for simplicity, we'll rely on a consistent internal processing order).
  • Attempting to embed a field into itself.

Examples

Example 1:

Input Data:
{
  "user_id": 123,
  "username": "john_doe",
  "email": "john.doe@example.com",
  "status": "active"
}

Transformation Rules:
{
  "id": {
    "type": "rename",
    "from": "user_id"
  },
  "profile": {
    "type": "embed",
    "fields": ["username", "email"]
  },
  "state": {
    "type": "rename",
    "from": "status"
  }
}

Output:
{
  "id": 123,
  "profile": {
    "username": "john_doe",
    "email": "john.doe@example.com"
  },
  "state": "active"
}

Explanation:
"user_id" was renamed to "id".
"username" and "email" were embedded into a new "profile" object.
"status" was renamed to "state".
"user_id" itself was not removed, only its name changed.

Example 2:

Input Data:
{
  "order_id": "ORD789",
  "customer_name": "Jane Smith",
  "items": [
    {"name": "Laptop", "price": 1200},
    {"name": "Mouse", "price": 25}
  ],
  "total_amount": 1225
}

Transformation Rules:
{
  "items": {
    "type": "remove"
  },
  "customer_info": {
    "type": "embed",
    "fields": ["customer_name"],
    "parent_field": "contact"
  },
  "amount": {
    "type": "rename",
    "from": "total_amount"
  }
}

Output:
{
  "order_id": "ORD789",
  "contact": {
    "customer_info": {
      "customer_name": "Jane Smith"
    }
  },
  "amount": 1225
}

Explanation:
The "items" field was removed.
"customer_name" was embedded into a new "customer_info" object, which was then embedded into a new "contact" object.
"total_amount" was renamed to "amount".

Example 3: Nested Structure and Removal

Input Data:
{
  "id": "A1",
  "data": {
    "value1": 100,
    "nested": {
      "keyA": "valA",
      "keyB": "valB"
    },
    "remove_me": "should_disappear"
  },
  "metadata": "some_info"
}

Transformation Rules:
{
  "nested_data": {
    "type": "embed",
    "fields": ["keyA"],
    "parent_field": "data"
  },
  "remove_me": {
    "type": "remove"
  },
  "metadata": {
    "type": "rename",
    "from": "original_metadata"
  }
}

Output:
{
  "id": "A1",
  "data": {
    "value1": 100,
    "nested": {
      "keyA": "valA"
    }
  },
  "original_metadata": "some_info"
}

Explanation:
Within the "data" object, "keyA" was embedded into "nested_data" (which itself is nested within "data").
The "remove_me" field was deleted from the "data" object.
"metadata" was renamed to "original_metadata".
Notice how transformations can be applied recursively to nested structures, and the "remove" rule targets the specific field to be deleted.

Constraints

  • Input JSON will contain primitive types (string, number, boolean, null) and nested objects. Arrays will be treated as opaque values and will not be transformed internally.
  • The depth of nested JSON objects will not exceed 20 levels.
  • The total number of fields in the input JSON (across all levels) will not exceed 1000.
  • The number of transformation rules will not exceed 100.
  • Performance: The transformation process should be reasonably efficient. For typical inputs, it should complete within a few milliseconds.

Notes

  • You'll likely need to use the encoding/json package for decoding and encoding JSON.
  • Recursive functions will be very helpful for handling nested structures.
  • Consider how you will manage the state of the data as transformations are applied. A good approach is to build a new map for the output rather than modifying the input map in place.
  • When embedding, if the parent_field specified in the rule already exists and is not an object, this should be handled gracefully (e.g., by creating a new parent if it doesn't exist, or potentially overwriting if it does). For this challenge, assume the parent_field will either not exist or will be a valid object to embed into.
  • The remove rule is designed to be applied to the current level of the JSON being processed. This means if a field is nested, you might need multiple remove rules or a recursive approach to targeting it. For this challenge, a single remove rule in the top-level rules map will attempt to remove that field from the top level. To remove nested fields, you'll need to specify rules for the parent object and then include a remove rule for the nested field within that parent's transformation definition. Example 3 demonstrates this for remove_me within data.
Loading editor...
go