@Override
  public XMLRecord buildRow(
      XMLRecord record,
      Object object,
      CoreAbstractSession session,
      Marshaller marshaller,
      XPathFragment rootFragment) {
    lazyInitialize();
    XPathNode textNode = rootXPathNode.getTextNode();
    List<XPathNode> nonAttributeChildren = rootXPathNode.getNonAttributeChildren();
    if (null == textNode && null == nonAttributeChildren) {
      return record;
    }

    Descriptor xmlDescriptor = (Descriptor) descriptor;
    XPathNode node = rootXPathNode;
    MarshalRecord marshalRecord = (MarshalRecord) record;
    QName schemaType = null;

    if (marshalRecord.getCycleDetectionStack().contains(object, marshaller.isEqualUsingIdenity())) {
      if (cycleRecoverableClass == null) {
        initCycleRecoverableClasses();
      }
      if (cycleRecoverableClass != null
          && cycleRecoverableClass.isAssignableFrom(object.getClass())) {
        try {
          Object jaxbMarshaller = marshaller.getProperty(Constants.JAXB_MARSHALLER);
          // Create a proxy instance of CycleRecoverable$Context, a parameter to
          // the onCycleDetected method
          Object contextProxy =
              CycleRecoverableContextProxy.getProxy(cycleRecoverableContextClass, jaxbMarshaller);
          // Invoke onCycleDetected method, passing in proxy, and reset
          // 'object' to the returned value
          Method onCycleDetectedMethod =
              object
                  .getClass()
                  .getMethod(ON_CYCLE_DETECTED, new Class[] {cycleRecoverableContextClass});
          object =
              PrivilegedAccessHelper.invokeMethod(
                  onCycleDetectedMethod, object, new Object[] {contextProxy});
        } catch (Exception e) {
          throw XMLMarshalException.marshalException(e);
        }

        // Returned object might have a different descriptor
        xmlDescriptor = (Descriptor) session.getDescriptor(object.getClass());
        if (xmlDescriptor != null) {
          node = ((ObjectBuilder) xmlDescriptor.getObjectBuilder()).getRootXPathNode();
        } else {
          node = null;
        }

        // Push new object
        marshalRecord.getCycleDetectionStack().push(object);

        // Write xsi:type if onCycleDetected returned an object of a type different than the one
        // mapped
        if (xmlDescriptor != descriptor) {
          if (xmlDescriptor == null) {
            schemaType = record.getConversionManager().schemaType(object.getClass());
          } else {
            schemaType = xmlDescriptor.getSchemaReference().getSchemaContextAsQName();
          }
          marshalRecord.writeXsiTypeAttribute(
              xmlDescriptor,
              schemaType.getNamespaceURI(),
              schemaType.getLocalPart(),
              schemaType.getPrefix(),
              false);
        }
      } else {
        // Push the duplicate object anyway, so that we can get the complete cycle string
        marshalRecord.getCycleDetectionStack().push(object);
        throw XMLMarshalException.objectCycleDetected(
            marshalRecord.getCycleDetectionStack().getCycleString());
      }
    } else {
      marshalRecord.getCycleDetectionStack().push(object);
    }

    NamespaceResolver namespaceResolver = null;
    if (xmlDescriptor != null) {
      namespaceResolver = xmlDescriptor.getNamespaceResolver();
    }
    MarshalContext marshalContext = null;
    if (xmlDescriptor != null && xmlDescriptor.isSequencedObject()) {
      SequencedObject sequencedObject = (SequencedObject) object;
      marshalContext = new SequencedMarshalContext(sequencedObject.getSettings());
    } else {
      marshalContext = ObjectMarshalContext.getInstance();
    }
    if (null == nonAttributeChildren) {
      textNode.marshal(
          (MarshalRecord) record,
          object,
          session,
          namespaceResolver,
          marshaller,
          marshalContext,
          rootFragment);
    } else {
      if (node == null) {
        // No descriptor for this object, so manually create a MappingNodeValue and marshal it
        XPathNode n = new XPathNode();
        CompositeObjectMapping m = new XMLCompositeObjectMapping();
        m.setXPath(".");
        XMLCompositeObjectMappingNodeValue nv = new XMLCompositeObjectMappingNodeValue(m);
        n.setMarshalNodeValue(nv);
        nv.marshalSingleValue(
            new XPathFragment("."),
            marshalRecord,
            null,
            object,
            session,
            namespaceResolver,
            marshalContext);
      } else {
        for (int x = 0, size = marshalContext.getNonAttributeChildrenSize(node); x < size; x++) {
          XPathNode xPathNode = (XPathNode) marshalContext.getNonAttributeChild(x, node);
          xPathNode.marshal(
              (MarshalRecord) record,
              object,
              session,
              namespaceResolver,
              marshaller,
              marshalContext.getMarshalContext(x),
              rootFragment);
        }
      }
    }
    marshalRecord.getCycleDetectionStack().pop();
    return record;
  }