class UnwrappedInfo {
    final String propertyName;
    final NameTransformer nameTransformer;
    final Set<String> beanPropertyNames;

    public UnwrappedInfo(DeserializationContext context, BeanPropertyDefinition unwrappedProperty) {
      propertyName = unwrappedProperty.getName();
      final JsonUnwrapped annotation = unwrappedProperty.getField().getAnnotation(JsonUnwrapped.class);
      nameTransformer = NameTransformer.simpleTransformer(annotation.prefix(), annotation.suffix());
      beanPropertyNames = new HashSet<>();
      // Extract viable property names for deserialization and nested deserialization
      final Set<Class<?>> processedTypes = new HashSet<>();
      extractPropertiesDeep(context, processedTypes, beanPropertyNames, unwrappedProperty);
    }

    private static void extractPropertiesDeep(DeserializationContext context, Set<Class<?>> processedTypes,
        Set<String> properties, BeanPropertyDefinition bean) {
      final Collection<NamedType> types = context.getConfig().getSubtypeResolver()
          .collectAndResolveSubtypesByClass(context.getConfig(),
              context.getConfig().introspect(bean.getPrimaryType()).getClassInfo());
      for (NamedType type : types) {
        if (!processedTypes.add(type.getType())) {
          continue;
        }
        for (BeanPropertyDefinition property : context.getConfig().introspect(context.constructType(type.getType()))
            .findProperties()) {
          properties.add(property.getName());
          extractPropertiesDeep(context, processedTypes, properties, property);
        }
      }
    }

  }

  @Override
  public JsonDeserializer<?> createContextual(DeserializationContext deserializationContext, BeanProperty beanProperty)
      throws JsonMappingException {
    return new JsonUnwrappedDeserializer<>(deserializationContext);
  }

  @Override
  public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {<FILL_FUNCTION_BODY>

    final ObjectNode node = jsonParser.readValueAsTree();
    final ObjectNode ownNode = deserializationContext.getNodeFactory().objectNode();
    final Map<UnwrappedInfo, ObjectNode> unwrappedNodes = new HashMap<>();

    node.fields().forEachRemaining(entry -> {
      final String key = entry.getKey();
      final JsonNode value = entry.getValue();

      boolean replaced = false;
      for (UnwrappedInfo unwrapped : unwrappedInfos) {
        final String transformed = unwrapped.nameTransformer.reverse(key);
        final ObjectNode unwrappedNode = unwrappedNodes.getOrDefault(unwrapped,
            deserializationContext.getNodeFactory().objectNode());
        if (transformed != null && !ownPropertyNames.contains(key) && unwrapped.beanPropertyNames.contains(transformed)) {
          unwrappedNodes.putIfAbsent(unwrapped, unwrappedNode);
          unwrappedNode.replace(transformed, value);
          replaced = true;
        }
      }
      if (!replaced && ownPropertyNames.contains(key)) {
        ownNode.replace(key, value);
      }
    });

    for (Map.Entry<UnwrappedInfo, ObjectNode> entry : unwrappedNodes.entrySet()) {
      ownNode.replace(entry.getKey().propertyName, entry.getValue());
    }

    try (TreeTraversingParser syntheticParser = new TreeTraversingParser(ownNode, jsonParser.getCodec())) {
      syntheticParser.nextToken();
      return beanDeserializer.deserialize(syntheticParser, deserializationContext);
    }
  
<no_super_class>