Why is my MERGE query deadlocking while in a serialized isolation level transaction?
If you want to ensure that only one process is allowed to run this MERGE query (stored procedure) at any given time, then 
sp_getapplock
is a good fit. It is very straight-forward, easy to understand and maintain, as opposed to obscure query hints. I'm not saying that it is impossible to achieve the same effect with hints. It is easier for me to understand the simple mutex.

This is a template of a stored procedure that I use. Adjust the timeout as needed. The caller should be aware of possible timeouts and retry if needed.

CREATE PROCEDURE MergeIt
    @dataToMerge MyTableType READONLY
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    BEGIN TRANSACTION
    BEGIN TRY

        DECLARE @VarLockResult int;
        EXEC @VarLockResult = sp_getapplock
            @Resource = 'MergeIt_app_lock',
            @LockMode = 'Exclusive',
            @LockOwner = 'Transaction',
            @LockTimeout = 60000,
            @DbPrincipal = 'public';

        IF @VarLockResult &gt;= 0
        BEGIN
            -- Acquired the lock
            MERGE INTO TargetTable WITH(HOLDLOCK) AS [target]
            USING @dataToMerge AS [source]
            ON [source].KeyPart_1 = [target].KeyPart_1 AND
               [source].KeyPart_2 = [target].KeyPart_2
            WHEN NOT MATCHED THEN
                INSERT(Data, KeyPart_1, KeyPart_2)
                VALUES([source].Data, [source].KeyPart_1, [source].KeyPart_2)
            WHEN MATCHED THEN
                UPDATE SET [target].Data = [source].Data
            ;

        END ELSE BEGIN
            -- timeout waiting for the lock
            -- TODO: handle the problem, e.g. return some error code,
            -- indicating that the caller should retry.
        END;

        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
        -- TODO: handle the problem. Return some error code?
    END CATCH;

    RETURN &lt;the error code&gt;
END