Hone logo
Hone
Problems

Go JSON Unmarshalling Challenge

You're tasked with building a core component for data processing in Go: a function to unmarshal JSON strings into Go data structures. This is a fundamental operation for working with APIs, configuration files, and inter-process communication. Your goal is to create a function that mimics the behavior of Go's standard encoding/json package's Unmarshal function, but with a specific focus on handling basic types and nested structures.

Problem Description

Implement a Go function named UnmarshalJSON that takes a JSON string and a pointer to a Go value as input. This function should parse the JSON string and populate the Go value with the corresponding data.

Key Requirements:

  • Basic Type Mapping: Correctly map JSON types (string, number, boolean, null) to their Go equivalents (string, float64, bool, nil). For numbers, assume they can be represented as float64 in Go.
  • Object Handling: Support unmarshalling JSON objects (key-value pairs) into Go structs. Field names in the JSON object should map to struct field names (case-insensitive matching is not required, exact match is fine).
  • Array Handling: Support unmarshalling JSON arrays into Go slices.
  • Nested Structures: Handle arbitrarily nested JSON objects and arrays.
  • Error Handling: Return an error if the JSON is invalid or if there's a type mismatch during unmarshalling.
  • Pointer Input: The Go value parameter must be a pointer, allowing the function to modify the original variable.

Expected Behavior:

  • If the JSON string represents a simple value (string, number, boolean), it should be assigned to the pointed-to variable.
  • If the JSON string represents an object, its fields should be mapped to the fields of the pointed-to struct.
  • If the JSON string represents an array, its elements should be mapped to the elements of the pointed-to slice.
  • If the JSON is malformed (e.g., missing closing brace, invalid syntax), an appropriate error should be returned.
  • If a JSON value's type does not match the Go variable's type (e.g., JSON string attempting to unmarshal into an int), an error should be returned.
  • If the input v is not a pointer, an error should be returned.

Edge Cases to Consider:

  • Empty JSON string.
  • JSON null value.
  • Empty JSON objects and arrays.
  • JSON keys with special characters.
  • Numeric values that might require careful handling (e.g., large numbers, decimals).

Examples

Example 1: Simple JSON Object

Input:

{
  "name": "Hone",
  "age": 30,
  "isMentor": true
}

Go Struct:

type Person struct {
	Name     string
	Age      float64
	IsMentor bool
}

Call: UnmarshalJSON([]byte({"name": "Hone", "age": 30, "isMentor": true}), &person)

Output:

Person{Name: "Hone", Age: 30.0, IsMentor: true}

Explanation: The JSON object's fields are mapped to the corresponding fields in the Person struct. The number 30 is unmarshalled as 30.0 into the float64 field Age.

Example 2: Nested JSON Object and Array

Input:

{
  "id": "abc-123",
  "details": {
    "city": "Techville",
    "zip": 98765
  },
  "tags": ["go", "ai", "coding"]
}

Go Struct:

type Project struct {
	ID      string
	Details struct {
		City string
		Zip  float64
	}
	Tags []string
}

Call: UnmarshalJSON([]byte({"id": "abc-123", "details": {"city": "Techville", "zip": 98765}, "tags": ["go", "ai", "coding"]}), &project)

Output:

Project{
  ID: "abc-123",
  Details: struct{City string; Zip float64}{City: "Techville", Zip: 98765.0},
  Tags: []string{"go", "ai", "coding"}
}

Explanation: The nested details object is unmarshalled into the nested struct, and the tags array is unmarshalled into the string slice.

Example 3: JSON Null and Type Mismatch

Input JSON:

{
  "value": null,
  "count": "five"
}

Go Struct:

type Data struct {
	Value *int
	Count float64
}

Call: UnmarshalJSON([]byte({"value": null, "count": "five"}), &data)

Expected Output: An error indicating a type mismatch for the Count field (JSON string "five" cannot be unmarshalled into a float64). Explanation: The Value field, being a pointer to an integer, will be set to nil because the JSON value is null. However, the Count field will cause an error because the JSON value "five" is a string and cannot be directly converted to a float64.

Constraints

  • The input JSON string will be valid UTF-8 encoded.
  • The function should be able to handle JSON strings up to 1MB in size.
  • Performance is important; the unmarshalling process should be reasonably efficient.
  • Do not use the encoding/json package's Unmarshal function in your solution. You can, however, use other functions from the encoding/json package for parsing if absolutely necessary (though a more fundamental implementation is preferred).
  • The Go value v will always be a pointer. You can assume v is not nil.

Notes

This challenge requires a deep understanding of JSON structure and Go's reflection capabilities to dynamically inspect and populate Go types. Consider how you will parse the JSON string token by token or segment by segment. Handling type conversions and nested structures will be the most complex parts. You'll likely need to use the reflect package extensively. Think about how to represent the JSON data internally before mapping it to your Go types.

Loading editor...
go