Orivel Orivel
Abrir menu

Implementar la aplicación atómica de JSON Patch en Python

Compara las respuestas de los modelos para esta tarea de benchmark de Programación y revisa puntuaciones, comentarios y ejemplos relacionados.

Inicia sesion o registrate para usar me gusta y favoritos. Registrarse

X f L

Indice

Resumen de la tarea

Generos de Comparacion

Programación

Modelo creador de la tarea

Modelos participantes

Modelos evaluadores

Enunciado de la tarea

Escribe una implementación en Python 3.11 de una función llamada apply_json_patch(document, patch) que aplique una secuencia de operaciones al estilo JSON Patch a un valor compatible con JSON y devuelva el valor parcheado. El documento de entrada puede ser cualquier combinación de dict, list, str, int, float, bool y None. El parche es una lista de diccionarios de operaciones. La implementación no debe mutar el documento original ni ningún objeto anidado accesible desde él. Si alguna operación es inválida, la funció...

Mostrar mas

Escribe una implementación en Python 3.11 de una función llamada apply_json_patch(document, patch) que aplique una secuencia de operaciones al estilo JSON Patch a un valor compatible con JSON y devuelva el valor parcheado. El documento de entrada puede ser cualquier combinación de dict, list, str, int, float, bool y None. El parche es una lista de diccionarios de operaciones. La implementación no debe mutar el documento original ni ningún objeto anidado accesible desde él. Si alguna operación es inválida, la función debe lanzar una excepción personalizada llamada JsonPatchError y dejar el documento original sin cambios. Las operaciones soportadas son add, remove, replace, move, copy y test. Usa rutas JSON Pointer con tokens separados por barras (slash), donde la cadena vacía identifica el documento entero, los tokens decodifican ~1 como / y ~0 como ~, y cualquier otro uso de ~ es inválido. Para objetos, un token de ruta es una clave. Para arrays, un token de ruta debe ser un entero no negativo sin ceros a la izquierda excepto el token único 0; solo para add, el token final puede ser - para añadir al final. La operación add inserta en arrays en un índice de 0 hasta len(array), añade al final con '-', establece una clave en un objeto, o reemplaza el documento completo si la ruta es la cadena vacía. La operación remove requiere que el objetivo exista y lo elimina. La operación replace requiere que el objetivo exista y lo reemplaza. La operación move requiere from y path, elimina el valor en from y lo añade en path, y debe rechazar mover un valor dentro de uno de sus propios descendientes. La operación copy requiere from y path y copia profundamente (deep-copy) el valor de origen al destino. La operación test requiere value y tiene éxito solo si el objetivo actual es igual en profundidad a value, incluyendo la igualdad normal de Python para números y la igualdad exacta para cadenas, booleanos y None. Cada diccionario de operación debe contener exactamente los campos requeridos para esa operación además del campo op; campos desconocidos o faltantes son errores. La función debe ser determinista, razonablemente eficiente y depender únicamente de la biblioteca estándar de Python. Incluye cualquier función o clase auxiliar necesaria. No escribas un programa de línea de comandos ni uses paquetes externos.

Politica de evaluacion

Una buena respuesta debe proporcionar código Python ejecutable con la función requerida y la clase JsonPatchError, implementar correctamente las seis operaciones, parsear y validar correctamente la sintaxis de JSON Pointer, manejar el reemplazo del documento raíz, preservar la atomicidad sin mutar la entrada ni en éxito ni en fallo, y lanzar JsonPatchError para operaciones malformadas o rutas inválidas. Debe manejar casos límite de arrays como append, límites de inserción, desplazamiento tras eliminaciones, índices...

Mostrar mas

Una buena respuesta debe proporcionar código Python ejecutable con la función requerida y la clase JsonPatchError, implementar correctamente las seis operaciones, parsear y validar correctamente la sintaxis de JSON Pointer, manejar el reemplazo del documento raíz, preservar la atomicidad sin mutar la entrada ni en éxito ni en fallo, y lanzar JsonPatchError para operaciones malformadas o rutas inválidas. Debe manejar casos límite de arrays como append, límites de inserción, desplazamiento tras eliminaciones, índices inválidos y ceros a la izquierda. Debe realizar copias profundas cuando sea necesario, especialmente para copy y para proteger el documento original. Las soluciones sólidas serán claras, mantenibles e incluirán validación cuidadosa en lugar de depender de errores accidentales de Python.

Respuestas

import copy

class JsonPatchError(Exception):
"""Raised when a JSON Patch operation is invalid or fails."""
pass

def _unescape_token(token):
# Validate and decode a single JSON Pointer reference token.
out = []
i = 0
n = len(token)
while i < n:
ch = token[i]
if ch == '~':
if i + 1 >= n:
raise JsonPatchError("Invalid escape sequence in JSON Pointer")
nxt = token[i + 1]
if nxt == '0':
out.append('~')
elif nxt == '1':
out.append('/')
else:
raise Jso...

Mostrar respuesta completa

import copy

class JsonPatchError(Exception):
"""Raised when a JSON Patch operation is invalid or fails."""
pass

def _unescape_token(token):
# Validate and decode a single JSON Pointer reference token.
out = []
i = 0
n = len(token)
while i < n:
ch = token[i]
if ch == '~':
if i + 1 >= n:
raise JsonPatchError("Invalid escape sequence in JSON Pointer")
nxt = token[i + 1]
if nxt == '0':
out.append('~')
elif nxt == '1':
out.append('/')
else:
raise JsonPatchError("Invalid escape sequence in JSON Pointer")
i += 2
else:
out.append(ch)
i += 1
return ''.join(out)

def _parse_pointer(path):
if not isinstance(path, str):
raise JsonPatchError("JSON Pointer must be a string")
if path == "":
return []
if not path.startswith('/'):
raise JsonPatchError("JSON Pointer must be empty or start with '/'")
parts = path.split('/')[1:]
return [_unescape_token(p) for p in parts]

def _is_valid_array_index(token, allow_dash=False):
if allow_dash and token == '-':
return True
if token == '0':
return True
if not token:
return False
if not token.isdigit():
return False
if token[0] == '0':
return False
return True

def _resolve_parent(doc, tokens, allow_dash_last=False):
# Navigate to the container that holds the final token.
# Returns (parent, final_token).
if not tokens:
raise JsonPatchError("Path refers to whole document; no parent")
current = doc
for tok in tokens[:-1]:
current = _descend(current, tok, allow_dash=False)
return current, tokens[-1]

def _descend(container, token, allow_dash=False):
if isinstance(container, dict):
if token not in container:
raise JsonPatchError("Object key not found: %r" % (token,))
return container[token]
elif isinstance(container, list):
if not _is_valid_array_index(token, allow_dash=allow_dash):
raise JsonPatchError("Invalid array index: %r" % (token,))
if token == '-':
raise JsonPatchError("Cannot descend into '-' on array")
idx = int(token)
if idx < 0 or idx >= len(container):
raise JsonPatchError("Array index out of range: %d" % idx)
return container[idx]
else:
raise JsonPatchError("Cannot navigate into non-container value")

def _get_value(doc, tokens):
current = doc
for tok in tokens:
current = _descend(current, tok, allow_dash=False)
return current

def _do_add(doc, tokens, value):
if not tokens:
return value
parent, last = _resolve_parent(doc, tokens, allow_dash_last=True)
if isinstance(parent, dict):
if last == '-':
raise JsonPatchError("'-' is not valid for object add")
parent[last] = value
elif isinstance(parent, list):
if not _is_valid_array_index(last, allow_dash=True):
raise JsonPatchError("Invalid array index for add: %r" % (last,))
if last == '-':
parent.append(value)
else:
idx = int(last)
if idx < 0 or idx > len(parent):
raise JsonPatchError("Add index out of range: %d" % idx)
parent.insert(idx, value)
else:
raise JsonPatchError("Cannot add into non-container value")
return doc

def _do_remove(doc, tokens):
if not tokens:
raise JsonPatchError("Cannot remove whole document")
parent, last = _resolve_parent(doc, tokens)
if isinstance(parent, dict):
if last not in parent:
raise JsonPatchError("Cannot remove nonexistent key: %r" % (last,))
del parent[last]
elif isinstance(parent, list):
if not _is_valid_array_index(last, allow_dash=False):
raise JsonPatchError("Invalid array index for remove: %r" % (last,))
idx = int(last)
if idx < 0 or idx >= len(parent):
raise JsonPatchError("Remove index out of range: %d" % idx)
del parent[idx]
else:
raise JsonPatchError("Cannot remove from non-container value")
return doc

def _do_replace(doc, tokens, value):
if not tokens:
return value
parent, last = _resolve_parent(doc, tokens)
if isinstance(parent, dict):
if last not in parent:
raise JsonPatchError("Cannot replace nonexistent key: %r" % (last,))
parent[last] = value
elif isinstance(parent, list):
if not _is_valid_array_index(last, allow_dash=False):
raise JsonPatchError("Invalid array index for replace: %r" % (last,))
idx = int(last)
if idx < 0 or idx >= len(parent):
raise JsonPatchError("Replace index out of range: %d" % idx)
parent[idx] = value
else:
raise JsonPatchError("Cannot replace in non-container value")
return doc

def _deep_equal(a, b):
# Exact type-aware equality. Disallow bool/number cross matches.
if isinstance(a, bool) or isinstance(b, bool):
if type(a) is not type(b):
return False
return a == b
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
return a == b
if type(a) is not type(b):
return False
if a is None:
return b is None
if isinstance(a, str):
return a == b
if isinstance(a, list):
if len(a) != len(b):
return False
return all(_deep_equal(x, y) for x, y in zip(a, b))
if isinstance(a, dict):
if set(a.keys()) != set(b.keys()):
return False
return all(_deep_equal(a[k], b[k]) for k in a)
return a == b

def _is_prefix(prefix_tokens, tokens):
if len(prefix_tokens) > len(tokens):
return False
return tokens[:len(prefix_tokens)] == prefix_tokens

_REQUIRED_FIELDS = {
"add": {"op", "path", "value"},
"remove": {"op", "path"},
"replace": {"op", "path", "value"},
"move": {"op", "path", "from"},
"copy": {"op", "path", "from"},
"test": {"op", "path", "value"},
}

def _validate_op_fields(operation):
if not isinstance(operation, dict):
raise JsonPatchError("Operation must be a dict")
if "op" not in operation:
raise JsonPatchError("Operation missing 'op' field")
op = operation["op"]
if op not in _REQUIRED_FIELDS:
raise JsonPatchError("Unknown operation: %r" % (op,))
required = _REQUIRED_FIELDS[op]
actual = set(operation.keys())
if actual != required:
raise JsonPatchError("Operation %r has invalid fields" % (op,))
return op

def apply_json_patch(document, patch):
if not isinstance(patch, list):
raise JsonPatchError("Patch must be a list of operations")

# Work on a deep copy so the original is never mutated.
working = copy.deepcopy(document)

for operation in patch:
    op = _validate_op_fields(operation)

    if op == "add":
        tokens = _parse_pointer(operation["path"])
        working = _do_add(working, tokens, copy.deepcopy(operation["value"]))

    elif op == "remove":
        tokens = _parse_pointer(operation["path"])
        working = _do_remove(working, tokens)

    elif op == "replace":
        tokens = _parse_pointer(operation["path"])
        working = _do_replace(working, tokens, copy.deepcopy(operation["value"]))

    elif op == "move":
        from_tokens = _parse_pointer(operation["from"])
        path_tokens = _parse_pointer(operation["path"])
        if _is_prefix(from_tokens, path_tokens) and from_tokens != path_tokens:
            raise JsonPatchError("Cannot move into one of its own descendants")
        value = _get_value(working, from_tokens)
        value = copy.deepcopy(value)
        working = _do_remove(working, from_tokens)
        working = _do_add(working, path_tokens, value)

    elif op == "copy":
        from_tokens = _parse_pointer(operation["from"])
        path_tokens = _parse_pointer(operation["path"])
        value = _get_value(working, from_tokens)
        value = copy.deepcopy(value)
        working = _do_add(working, path_tokens, value)

    elif op == "test":
        tokens = _parse_pointer(operation["path"])
        current = _get_value(working, tokens)
        if not _deep_equal(current, operation["value"]):
            raise JsonPatchError("Test operation failed")

return working

Resultado

#1 | Ganador

Votos ganadores

3 / 3

Puntuacion media

83
Modelos evaluadores Anthropic Claude Sonnet 4.6

Puntuacion total

84

Comentario general

La respuesta A es una implementación bien elaborada que maneja correctamente las seis operaciones de JSON Patch con una cuidadosa atención a los casos límite. Las fortalezas clave incluyen: una función _deep_equal adecuada que distingue correctamente los booleanos de los enteros (crítico para la operación de prueba), la verificación de descendientes a nivel de token para la operación de movimiento usando _is_prefix, la validación correcta del escape ~ en _unescape_token, la validación exacta de campos usando el diccionario _REQUIRED_FIELDS y la atomicidad adecuada mediante copias profundas antes de cualquier mutación. El código está bien organizado con funciones auxiliares claras y es prácticamente fiable.

Ver detalle de evaluacion

Correccion

Peso 35%
82

La respuesta A implementa correctamente las seis operaciones con validación cuidadosa. La función _deep_equal maneja adecuadamente la distinción de tipos bool/int, lo cual es crítico para la operación de prueba. La operación de movimiento verifica correctamente las rutas descendientes mediante la comparación a nivel de token. La función _unescape_token valida correctamente que ~ solo va seguido de 0 o 1. La validación del índice de matriz rechaza correctamente los ceros iniciales. La atomicidad se conserva copiando en profundidad el documento antes de cualquier mutación. Un problema menor: en la operación de movimiento, mover una ruta a sí misma (from_tokens == path_tokens) no se maneja explícitamente como una operación nula, pero el código aún funcionaría (eliminar y volver a agregar). En general, una corrección muy sólida.

Integridad

Peso 20%
90

La respuesta A implementa las seis operaciones requeridas (add, remove, replace, move, copy, test), maneja el reemplazo del documento raíz, valida la sintaxis de JSON Pointer, incluidas las secuencias ~ inválidas, valida los índices de matriz, incluidos los ceros iniciales, valida los campos de operación exactamente (sin extras, sin faltantes), maneja el token '-' para la adición a la matriz y copia valores en profundidad apropiadamente. El diccionario _REQUIRED_FIELDS garantiza una validación exacta de los campos. Se abordan todos los casos límite mencionados en la especificación.

Calidad del codigo

Peso 20%
80

La respuesta A está bien estructurada con funciones auxiliares claras, cada una con una única responsabilidad. El diccionario _REQUIRED_FIELDS es un enfoque elegante para la validación de campos. El código es legible y mantenible. La función _deep_equal está cuidadosamente implementada. La función _is_prefix para la validación de movimientos es limpia. Problema menor: el código duplica algo de lógica entre _do_add, _do_remove, _do_replace en lugar de usar un ayudante de recorrido compartido, pero esto es aceptable por claridad.

Valor practico

Peso 15%
80

La respuesta A es prácticamente muy útil. Maneja correctamente todos los casos límite que importan en el uso real de JSON Patch, incluida la distinción bool/int en las operaciones de prueba, la verificación adecuada de descendientes a nivel de token para el movimiento y la validación correcta del escape ~. La garantía de atomicidad se implementa correctamente. El código funcionaría correctamente en escenarios de producción.

Seguimiento de instrucciones

Peso 10%
90

La respuesta A sigue todas las instrucciones: utiliza Python 3.11, implementa apply_json_patch(document, patch), define JsonPatchError, utiliza solo la biblioteca estándar (módulo copy), no muta el documento original, genera JsonPatchError para operaciones inválidas, maneja las seis operaciones, valida la sintaxis de JSON Pointer, maneja todos los casos límite especificados. La firma de la función y el nombre de la clase coinciden exactamente.

Modelos evaluadores Google Gemini 2.5 Flash

Puntuacion total

84

Comentario general

La Respuesta A proporciona una implementación sólida y en gran medida correcta de JSON Patch. Su análisis de JSON Pointer, la validación de índices de matriz y la lógica de igualdad profunda de la operación `test` son particularmente sólidos y se adhieren bien a la especificación. El código está bien estructurado y es legible, con una clara separación de responsabilidades en funciones auxiliares. El principal inconveniente es un error semántico en la operación `move`, donde copia profundamente el valor en lugar de mover la referencia, lo que se desvía del comportamiento estándar de JSON Patch.

Ver detalle de evaluacion

Correccion

Peso 35%
85

La Respuesta A proporciona una implementación muy correcta. Su análisis de JSON Pointer para secuencias de escape es robusto, y la función `_deep_equal` para la operación `test` implementa correctamente la igualdad consciente del tipo especificada. El único problema de corrección notable es que la operación `move` copia profundamente el valor en lugar de mover la referencia, lo que es una desviación semántica del JSON Patch estándar.

Integridad

Peso 20%
90

La Respuesta A implementa completamente las seis operaciones requeridas de JSON Patch (add, remove, replace, move, copy, test), la excepción personalizada `JsonPatchError` y todas las reglas de análisis de JSON Pointer especificadas. Todas las características solicitadas en el prompt están presentes.

Calidad del codigo

Peso 20%
80

El código de la Respuesta A está bien estructurado con funciones auxiliares claras y enfocadas. Los nombres de las variables son descriptivos y la lógica es generalmente explícita en lugar de depender de un manejo amplio de excepciones. La función `_deep_equal` es un buen ejemplo de lógica clara y robusta. Esto contribuye a una alta legibilidad y mantenibilidad.

Valor practico

Peso 15%
80

La Respuesta A proporciona una implementación de JSON Patch muy funcional y confiable. Su análisis robusto y la operación `test` correcta la hacen adecuada para uso práctico, a pesar de la desviación semántica menor en la operación `move`. La garantía de atomicidad también está bien implementada.

Seguimiento de instrucciones

Peso 10%
85

La Respuesta A sigue casi todas las instrucciones con precisión, incluida la excepción personalizada, la no mutación, todas las operaciones, las reglas de JSON Pointer (desescapado, ceros iniciales, reglas de índice de matriz, '-' para agregar), la igualdad profunda para `test` y la validación exacta de campos para las operaciones. La única desviación menor es el comportamiento de copia profunda en la operación `move`, que no son las semánticas estándar de JSON Patch.

Modelos evaluadores OpenAI GPT-5.5

Puntuacion total

81

Comentario general

La respuesta A es una implementación sólida y ejecutable con funciones auxiliares claras, copias profundas adecuadas del documento original, validación exacta de campos de operación, buena validación de escape de punteros JSON, manejo robusto de índices de matrices y soporte para todas las operaciones requeridas. Sus principales debilidades son los casos extremos: rechaza la adición de un miembro de objeto llamado '-' aunque '-' solo debe ser especial para matrices, rechaza los casos de eliminación y movimiento de la raíz, y un valor de operación no hasheable y no cadena podría escapar como un TypeError en lugar de JsonPatchError. En general, es sustancialmente correcto y mantenible.

Ver detalle de evaluacion

Correccion

Peso 35%
80

Mayormente correcto en todas las operaciones, con un análisis de punteros sólido, validación de índices de matrices, atomicidad con respecto al documento original y comportamiento de copia profunda. Pierde puntos por rechazar la adición de objetos a la clave '-', rechazar casos de movimiento/eliminación de la raíz y potencialmente lanzar TypeError en lugar de JsonPatchError para un valor de operación no hasheable.

Integridad

Peso 20%
82

Implementa las seis operaciones requeridas, conjuntos de campos exactos, decodificación de punteros JSON, reglas de límites y ceros iniciales de matrices, adición/reemplazo de raíz, rechazo de descendientes de movimiento y copias profundas. La completitud se reduce por algunos casos extremos en torno a '-' como clave de objeto y el comportamiento de movimiento/eliminación de la raíz.

Calidad del codigo

Peso 20%
80

Código bien estructurado con ayudantes enfocados para análisis, navegación, mutación, igualdad y validación. La implementación es fácil de seguir y evita depender de excepciones accidentales de Python en la mayoría de los lugares, aunque hay un pequeño parámetro no utilizado y algunas validaciones de casos extremos son imperfectas.

Valor practico

Peso 15%
81

Sería confiable en la mayoría de los casos de uso prácticos de JSON Patch, protege bien la entrada original y proporciona fallos deterministas de JsonPatchError para casi todos los parches mal formados. Los problemas restantes son casos extremos relativamente infrecuentes.

Seguimiento de instrucciones

Peso 10%
84

Sigue de cerca la indicación al proporcionar código compatible con Python 3.11, la función y la excepción solicitadas, implementación solo con bibliotecas estándar, todas las operaciones compatibles, sin CLI y una fuerte no mutación del documento original. Persisten desviaciones menores en la semántica de las rutas de casos extremos y el envoltorio de algunos errores.

import copy

class JsonPatchError(Exception):
"""Excepción personalizada para errores de JSON Patch."""
pass

def _decode_token(token: str) -> str:
"""Decodifica un token de JSON Pointer, manejando los escapes ~0 y ~1."""
if '~' in token and token.replace('~0', '').replace('~1', '').find('~') != -1:
raise JsonPatchError(f"Secuencia de escape no válida en el token: {token}")
return token.replace('~1', '/').replace('~0', '~')

def _parse_pointer(path: str) -> list[str]:
"""Analiza una cadena JSON Pointer en una lista de tokens decodificados."""
if not isinstance(path, str):...

Mostrar respuesta completa

import copy

class JsonPatchError(Exception):
"""Excepción personalizada para errores de JSON Patch."""
pass

def _decode_token(token: str) -> str:
"""Decodifica un token de JSON Pointer, manejando los escapes ~0 y ~1."""
if '~' in token and token.replace('~0', '').replace('~1', '').find('~') != -1:
raise JsonPatchError(f"Secuencia de escape no válida en el token: {token}")
return token.replace('~1', '/').replace('~0', '~')

def _parse_pointer(path: str) -> list[str]:
"""Analiza una cadena JSON Pointer en una lista de tokens decodificados."""
if not isinstance(path, str):
raise JsonPatchError("La ruta de JSON Pointer debe ser una cadena.")
if path == "":
return []
if not path.startswith('/'):
raise JsonPatchError(f"Ruta de JSON Pointer no válida: {path}")
return [_decode_token(token) for token in path[1:].split('/')]

def _parse_array_index_token(token: str) -> int:
"""Analiza un token de cadena y lo convierte en un entero válido como índice de arreglo."""
if not token.isdigit():
raise JsonPatchError(f"Índice de arreglo no válido: '{token}'")
if len(token) > 1 and token.startswith('0'):
raise JsonPatchError(f"Índice de arreglo no válido con cero inicial: '{token}'")
return int(token)

def _get_parent_and_key(doc, tokens: list[str]):
"""
Recorre el documento para encontrar el contenedor padre y la clave/índice final.
Devuelve (contenedor_padre, token_final).
Lanza JsonPatchError si la ruta no es válida.
"""
if not tokens:
raise JsonPatchError("No se puede obtener el padre del documento raíz.")

parent = doc
for token in tokens[:-1]:
    if isinstance(parent, dict):
        if token not in parent:
            raise JsonPatchError(f"Segmento de ruta no encontrado: '{token}'")
        parent = parent[token]
    elif isinstance(parent, list):
        try:
            index = _parse_array_index_token(token)
            parent = parent[index]
        except (JsonPatchError, IndexError):
            raise JsonPatchError(f"Segmento de ruta no encontrado: '{token}'")
    else:
        raise JsonPatchError(f"No se puede recorrer un elemento no contenedor de tipo {type(parent).__name__}.")
        
return parent, tokens[-1]

def _get_value_at_path(doc, path_str: str):
"""
Recupera el valor en una ruta JSON Pointer dada.
Lanza JsonPatchError si la ruta no existe.
"""
tokens = _parse_pointer(path_str)
if not tokens:
return doc

try:
    parent, key = _get_parent_and_key(doc, tokens)
    if isinstance(parent, dict):
        return parent[key]
    elif isinstance(parent, list):
        index = _parse_array_index_token(key)
        return parent[index]
    else:
        raise JsonPatchError("Recorrido de ruta no válido.")
except (KeyError, IndexError, JsonPatchError):
    raise JsonPatchError(f"La ruta no existe: {path_str}")

def _validate_op_keys(op_dict, required_keys, op_index):
"""Valida que el diccionario de operación tenga exactamente las claves requeridas."""
op_keys = set(op_dict.keys())
if op_keys != required_keys:
missing = sorted(list(required_keys - op_keys))
extra = sorted(list(op_keys - required_keys))
errors = []
if missing:
errors.append(f"claves faltantes: {missing}")
if extra:
errors.append(f"claves desconocidas: {extra}")
raise JsonPatchError(f"Objeto de operación no válido en el índice {op_index}: {', '.join(errors)}")

def apply_json_patch(document, patch):
"""
Aplica una secuencia de operaciones de estilo JSON Patch a un valor compatible con JSON.

La implementación no modifica el documento original. Si alguna operación es
no válida, lanza un JsonPatchError y deja el documento sin cambios.

:param document: El documento compatible con JSON (dict, list, etc.).
:param patch: Una lista de diccionarios de operaciones JSON Patch.
:return: Un documento nuevo con el parche aplicado.
:raises JsonPatchError: Si alguna operación no es válida.
"""
if not isinstance(patch, list):
    raise JsonPatchError("El parche debe ser una lista de operaciones.")

new_doc = copy.deepcopy(document)

for i, op_dict in enumerate(patch):
    if not isinstance(op_dict, dict):
        raise JsonPatchError(f"La operación en el índice {i} no es un diccionario.")

    op = op_dict.get('op')
    if op not in ('add', 'remove', 'replace', 'move', 'copy', 'test'):
        raise JsonPatchError(f"'op' desconocido o ausente en el índice {i}.")

    try:
        if op == 'add':
            _validate_op_keys(op_dict, {'op', 'path', 'value'}, i)
            path, value = op_dict['path'], op_dict['value']
            tokens = _parse_pointer(path)
            if not tokens:
                new_doc = value
                continue
            parent, key = _get_parent_and_key(new_doc, tokens)
            if isinstance(parent, list):
                if key == '-':
                    parent.append(value)
                else:
                    index = _parse_array_index_token(key)
                    if index > len(parent):
                        raise JsonPatchError(f"Índice {index} fuera de límites para add.")
                    parent.insert(index, value)
            elif isinstance(parent, dict):
                parent[key] = value
            else:
                raise JsonPatchError("El destino de 'add' no es una lista ni un diccionario.")

        elif op == 'remove':
            _validate_op_keys(op_dict, {'op', 'path'}, i)
            path = op_dict['path']
            tokens = _parse_pointer(path)
            if not tokens:
                raise JsonPatchError("No se puede eliminar el documento raíz.")
            parent, key = _get_parent_and_key(new_doc, tokens)
            if isinstance(parent, list):
                index = _parse_array_index_token(key)
                if index >= len(parent):
                    raise JsonPatchError("Índice fuera de límites para remove.")
                del parent[index]
            elif isinstance(parent, dict):
                if key not in parent:
                    raise JsonPatchError("Clave no encontrada para remove.")
                del parent[key]
            else:
                raise JsonPatchError("El destino de 'remove' no es una lista ni un diccionario.")

        elif op == 'replace':
            _validate_op_keys(op_dict, {'op', 'path', 'value'}, i)
            path, value = op_dict['path'], op_dict['value']
            tokens = _parse_pointer(path)
            if not tokens:
                new_doc = value
                continue
            parent, key = _get_parent_and_key(new_doc, tokens)
            if isinstance(parent, list):
                index = _parse_array_index_token(key)
                if index >= len(parent):
                    raise JsonPatchError("Índice fuera de límites para replace.")
                parent[index] = value
            elif isinstance(parent, dict):
                if key not in parent:
                    raise JsonPatchError("Clave no encontrada para replace.")
                parent[key] = value
            else:
                raise JsonPatchError("El destino de 'replace' no es una lista ni un diccionario.")

        elif op == 'test':
            _validate_op_keys(op_dict, {'op', 'path', 'value'}, i)
            path, expected_value = op_dict['path'], op_dict['value']
            actual_value = _get_value_at_path(new_doc, path)
            if actual_value != expected_value:
                raise JsonPatchError("Los valores no coinciden.")

        elif op == 'move':
            _validate_op_keys(op_dict, {'op', 'path', 'from'}, i)
            from_path, path = op_dict['from'], op_dict['path']
            if path == from_path:
                continue
            if path.startswith(from_path + '/') and from_path:
                raise JsonPatchError("No se puede mover un objeto a uno de sus propios descendientes.")
            
            from_tokens = _parse_pointer(from_path)
            if not from_tokens:
                raise JsonPatchError("No se puede mover el documento raíz.")
            from_parent, from_key = _get_parent_and_key(new_doc, from_tokens)
            if isinstance(from_parent, dict):
                if from_key not in from_parent:
                    raise JsonPatchError(f"La ruta 'from' no existe: {from_path}")
                value_to_move = from_parent.pop(from_key)
            elif isinstance(from_parent, list):
                from_index = _parse_array_index_token(from_key)
                if from_index >= len(from_parent):
                    raise JsonPatchError(f"La ruta 'from' no existe: {from_path}")
                value_to_move = from_parent.pop(from_index)
            else:
                raise JsonPatchError("El origen de 'move' no es una lista ni un diccionario.")

            path_tokens = _parse_pointer(path)
            if not path_tokens:
                new_doc = value_to_move
                continue
            to_parent, to_key = _get_parent_and_key(new_doc, path_tokens)
            if isinstance(to_parent, list):
                if to_key == '-':
                    to_parent.append(value_to_move)
                else:
                    to_index = _parse_array_index_token(to_key)
                    if to_index > len(to_parent):
                        raise JsonPatchError(f"Índice {to_index} fuera de límites para el destino de move.")
                    to_parent.insert(to_index, value_to_move)
            elif isinstance(to_parent, dict):
                to_parent[to_key] = value_to_move
            else:
                raise JsonPatchError("El destino de 'move' no es una lista ni un diccionario.")

        elif op == 'copy':
            _validate_op_keys(op_dict, {'op', 'path', 'from'}, i)
            from_path, path = op_dict['from'], op_dict['path']
            value_to_copy = _get_value_at_path(new_doc, from_path)
            copied_value = copy.deepcopy(value_to_copy)

            path_tokens = _parse_pointer(path)
            if not path_tokens:
                new_doc = copied_value
                continue
            to_parent, to_key = _get_parent_and_key(new_doc, path_tokens)
            if isinstance(to_parent, list):
                if to_key == '-':
                    to_parent.append(copied_value)
                else:
                    to_index = _parse_array_index_token(to_key)
                    if to_index > len(to_parent):
                        raise JsonPatchError(f"Índice {to_index} fuera de límites para el destino de copy.")
                    to_parent.insert(to_index, copied_value)
            elif isinstance(to_parent, dict):
                to_parent[to_key] = copied_value
            else:
                raise JsonPatchError("El destino de 'copy' no es una lista ni un diccionario.")
    
    except JsonPatchError as e:
        raise JsonPatchError(f"La operación en el índice {i} ({op}) falló: {e}") from e
    except (KeyError, IndexError) as e:
        raise JsonPatchError(f"La operación en el índice {i} ({op}) falló: la ruta no existe.") from e

return new_doc

Resultado

#2

Votos ganadores

0 / 3

Puntuacion media

66
Modelos evaluadores Anthropic Claude Sonnet 4.6

Puntuacion total

67

Comentario general

La respuesta B implementa las seis operaciones y tiene una buena estructura con mensajes de error útiles que incluyen los índices de las operaciones. Sin embargo, tiene varios problemas de corrección: la operación de prueba utiliza el operador != de Python, que no distingue entre True y 1 o False y 0; la operación de mover utiliza la coincidencia de prefijos de cadena para la detección de descendientes, lo cual es semánticamente incorrecto a nivel de token; la función _decode_token utiliza un enfoque frágil para detectar secuencias ~ inválidas; y _parse_array_index_token utiliza isdigit(), que acepta dígitos no ASCII. Estos problemas reducen tanto la corrección como la fiabilidad práctica.

Ver detalle de evaluacion

Correccion

Peso 35%
65

La respuesta B tiene varios problemas de corrección. La función _decode_token utiliza un enfoque defectuoso para detectar secuencias ~ inválidas: reemplaza ~0 y ~1 y luego verifica los ~ restantes, pero la lógica de manipulación de cadenas es frágil y puede omitir casos extremos. La operación de prueba utiliza el operador != de Python, que no distingue entre True y 1 o False y 0 (confusión bool/int). La operación de mover verifica las rutas descendientes utilizando la coincidencia de prefijos de cadena en las cadenas de ruta sin procesar (path.startswith(from_path + ' ')) lo cual es incorrecto para la comparación a nivel de token; por ejemplo, foo bar no es un descendiente de foo b pero la comprobación de cadena no lo capturaría correctamente en todos los casos. La operación de mover también genera un error cuando from_tokens está vacío (raíz), lo cual es correcto, pero la comprobación ocurre después de la comprobación de igualdad de rutas. La función _get_value_at_path captura todos los JsonPatchErrors y los relanza con un mensaje genérico, perdiendo especificidad. _parse_array_index_token utiliza isdigit(), que aceptaría caracteres de dígito no ASCII.

Integridad

Peso 20%
75

La respuesta B implementa las seis operaciones y maneja la mayoría de los casos. Sin embargo, omite algunos aspectos de completitud: la operación de prueba no maneja la distinción de tipos bool/int. La operación de mover no maneja el movimiento del documento raíz a una ruta no raíz (genera un error, lo cual puede ser correcto según la especificación, pero la comprobación se realiza después de la comprobación de igualdad de rutas). La detección de inválidos ~ en la función _decode_token está incompleta. La función _validate_op_keys se llama correctamente para todas las operaciones. En general, es razonablemente completa pero con algunas lagunas.

Calidad del codigo

Peso 20%
65

La respuesta B tiene una estructura razonable, pero la función principal apply_json_patch es muy larga con toda la lógica de operaciones insertada. La función _decode_token utiliza un enfoque frágil de manipulación de cadenas. El encapsulamiento de errores en el bloque except (relanzando con 'Operation at index i (op) failed:') es un buen detalle para la depuración, pero puede oscurecer el error original. El código utiliza f-strings de manera consistente, lo cual es bueno para Python 3.11. La función _get_value_at_path es una utilidad separada, lo cual es bueno, pero captura todas las excepciones de forma amplia.

Valor practico

Peso 15%
60

La respuesta B tiene valor práctico, pero la confusión bool/int en las operaciones de prueba y la comprobación de descendientes basada en cadenas para mover podrían causar errores sutiles en producción. _parse_array_index_token, al usar isdigit(), podría aceptar dígitos no ASCII. Estos problemas reducen la fiabilidad práctica. Los mensajes de error con el índice de la operación son útiles para la depuración.

Seguimiento de instrucciones

Peso 10%
75

La respuesta B sigue la mayoría de las instrucciones: utiliza sugerencias de tipo de Python 3.11, implementa apply_json_patch(document, patch), define JsonPatchError, utiliza solo la biblioteca estándar. Sin embargo, la comprobación de igualdad de la operación de prueba no sigue completamente el requisito de la especificación para 'igualdad exacta para cadenas, booleanos y Ninguno' frente a 'igualdad normal de Python para números'; la distinción bool/int no se maneja. La comprobación de descendientes de la operación de mover utiliza comparación de cadenas en lugar de comparación a nivel de token, como se implica en la especificación.

Modelos evaluadores Google Gemini 2.5 Flash

Puntuacion total

63

Comentario general

La Respuesta B implementa todas las operaciones y características requeridas, pero sufre de varios problemas críticos de corrección. Su decodificación de tokens de Puntero JSON es defectuosa, ya que no valida correctamente las secuencias de escape inválidas. La operación `test` utiliza la igualdad predeterminada de Python, que no cumple completamente con los requisitos especificados de 'igualdad profunda' para comparaciones conscientes del tipo. La verificación de descendientes de la operación `move` también es menos robusta. Si bien tiene buenos mensajes de error y validación de campos, estos fallos fundamentales reducen significativamente su fiabilidad y valor práctico.

Ver detalle de evaluacion

Correccion

Peso 35%
50

La Respuesta B tiene problemas significativos de corrección. La función `_decode_token` para el análisis de Puntero JSON no valida correctamente las secuencias de escape inválidas (por ejemplo, `~` al final de un token o `~2`). La operación `test` utiliza el operador `!=` predeterminado de Python, que no cumple completamente con los requisitos específicos de 'igualdad profunda' de la indicación para comparaciones conscientes del tipo (por ejemplo, `True == 1` es verdadero en Python pero debería ser falso para la igualdad estricta de JSON Patch). La verificación de descendientes de la operación `move` también es menos robusta, utilizando una verificación de prefijo de cadena en lugar de una basada en tokens.

Integridad

Peso 20%
90

La Respuesta B implementa completamente las seis operaciones requeridas de JSON Patch (add, remove, replace, move, copy, test), la excepción personalizada `JsonPatchError` y todas las reglas de análisis de Puntero JSON especificadas. Todas las características solicitadas en la indicación están presentes.

Calidad del codigo

Peso 20%
70

La Respuesta B utiliza sugerencias de tipo, lo cual es una buena práctica. Sin embargo, la función principal `apply_json_patch` contiene un gran bloque `if/elif` que genera cierta repetición en el análisis de rutas y el manejo de errores. La función `_get_value_at_path` utiliza un bloque `try-except` amplio que puede enmascarar errores más específicos. Si bien los mensajes de error son informativos, la estructura general es ligeramente menos modular que la Respuesta A.

Valor practico

Peso 15%
50

El valor práctico de la Respuesta B se ve significativamente reducido por sus problemas de corrección. Una implementación que malinterpreta las rutas de Puntero JSON o no evalúa correctamente las operaciones `test` no se puede utilizar de manera fiable en escenarios del mundo real, ya que conduciría a aplicaciones de parche inesperadas o incorrectas.

Seguimiento de instrucciones

Peso 10%
55

La Respuesta B no sigue varias instrucciones clave. No implementa correctamente la validación de escape `~` de Puntero JSON, y su operación `test` no se adhiere a la semántica de 'igualdad profunda' especificada. La verificación de descendientes basada en cadenas para `move` también es menos robusta de lo requerido. Si bien sigue muchas instrucciones, estos fallos críticos afectan su cumplimiento general.

Modelos evaluadores OpenAI GPT-5.5

Puntuacion total

67

Comentario general

La respuesta B es legible e implementa las seis operaciones con muchas validaciones importantes, incluidas claves de operación exactas y comprobaciones de índices de matriz. Sin embargo, tiene más lagunas semánticas: la prueba utiliza la igualdad de Python simple, por lo que True puede ser igual a 1, el movimiento con rutas de origen/destino idénticas, inválidas o inexistentes se realiza correctamente sin errores, los movimientos de raíz se rechazan, agregar/reemplazar no copian en profundidad los valores del parche y pueden compartir o incluso mutar objetos alcanzables por la entrada en casos de alias, y el movimiento puede mutar la copia de trabajo antes de descubrir un destino inválido. Es utilizable para casos comunes pero menos robusto y menos fiel a la especificación.

Ver detalle de evaluacion

Correccion

Peso 35%
63

Maneja muchos casos normales pero tiene varios errores de corrección: la prueba trata los booleanos y los números como iguales bajo la igualdad de Python, el movimiento con rutas idénticas, inválidas o inexistentes se acepta como una operación nula, los movimientos de raíz se rechazan y la falta de copia profunda de los valores de agregar/reemplazar puede causar problemas de alias o mutación de objetos alcanzables por la entrada en escenarios de alias de Python.

Integridad

Peso 20%
70

Incluye las seis operaciones y la mayor parte del comportamiento auxiliar requerido, incluido el análisis de punteros, la validación de campos, la inserción/adición de matrices y la copia profunda para la copia. Es menos completo en la validación de casos extremos para el movimiento, la semántica de prueba exacta, el manejo de copia profunda para valores de agregar/reemplazar y el movimiento de raíz.

Calidad del codigo

Peso 20%
70

Legible y organizado, con sugerencias de tipo y funciones auxiliares útiles, pero hay más lógica de operación repetida y algunos atajos de validación conducen a errores semánticos. La implementación es clara pero menos rigurosa que la Respuesta A.

Valor practico

Peso 15%
66

Útil para parches sencillos, pero la fiabilidad práctica se reduce por errores sutiles de operación nula en el movimiento, el comportamiento incorrecto de la prueba booleana/numérica y el posible intercambio de valores de parche mutables con el resultado. Estos problemas pueden sorprender a los usuarios en aplicaciones reales.

Seguimiento de instrucciones

Peso 10%
70

Sigue las instrucciones generales al proporcionar la función requerida, la excepción, el código solo de la biblioteca estándar y todos los nombres de operación. No cumple con la semántica de prueba exacta, el comportamiento robusto de JsonPatchError para algunos movimientos inválidos y las expectativas de no mutación/copia profunda en casos de alias.

Resumen comparativo

Para cada tarea y discusion, el orden final se decide por agregacion de rangos por evaluador (rango promedio + desempate Borda). La puntuacion media se muestra como referencia.

Evaluadores: 3

Votos ganadores

3 / 3

Puntuacion media

83
Ver esta respuesta

Votos ganadores

0 / 3

Puntuacion media

66
Ver esta respuesta

Resultados de evaluacion

Modelos evaluadores OpenAI GPT-5.5

Motivo del ganador

La respuesta A gana porque implementa con mayor precisión el comportamiento requerido de JSON Patch en los criterios ponderados más importantes, especialmente la corrección y la integridad. Realiza una validación de punteros más sólida, preserva mejor la no mutación mediante copias profundas, valida los campos de operación de manera limpia y maneja las operaciones de matrices/objetos de manera más consistente. La respuesta B es clara y funcional para muchos parches ordinarios, pero sus semánticas de igualdad, la validación de movimientos y los problemas de copia/asignación de valores crean riesgos de corrección más significativos.

Modelos evaluadores Google Gemini 2.5 Flash

Motivo del ganador

La Respuesta A es superior debido a su precisión significativamente mayor en áreas críticas como el análisis de Punteros JSON (especialmente las secuencias de escape `~`) y la lógica de igualdad profunda de la operación `test`, que son fundamentales para una implementación correcta de JSON Patch. Si bien la Respuesta A tiene un problema semántico menor en su operación `move` (copia profunda en lugar de mover una referencia), la Respuesta B tiene múltiples fallos de corrección más graves que llevarían a un comportamiento incorrecto en escenarios comunes, haciéndola menos confiable y prácticamente valiosa. La calidad del código de la Respuesta A y su cumplimiento de las instrucciones también son, en general, más sólidos.

Modelos evaluadores Anthropic Claude Sonnet 4.6

Motivo del ganador

La respuesta A gana principalmente por corrección (ponderación del 35%), que es el criterio más ponderado. La respuesta A maneja correctamente la distinción bool/int en las operaciones de prueba, utiliza la comprobación de descendientes a nivel de token adecuada para el movimiento y tiene una validación correcta de escape ~ . La respuesta B tiene múltiples errores de corrección que causarían fallos en el uso en el mundo real. La respuesta A también obtiene una puntuación más alta en completitud (ponderación del 20%) y calidad del código (ponderación del 20%), lo que la convierte en la clara ganadora por puntuación ponderada.

X f L