I see several spelling errors in the column names (e.g., hasStates in the XML fragment, but you are quering for hasState). Careful about the case as well - XML is case sensitive even if your server/database has case insensitive collation. Also, I didn't follow the logic of getting the first value of code, second value of hasStates etc. (code vs hasStates etc.) In any case, try the following and see if it is any better.
T.C.value('./@code', 'varchar(2)') as Country,
T.C.value('./@hasStates', 'int') as St,
T.C.value('./@hasPostal','int') as Postal
FROM ( SELECT CONVERT(XML, BulkColumn ) AS Data
from openrowset(bulk 'C:\Data\Postal.xml', single_blob) as I
) as x
cross apply data.nodes('Countries/Country') as T(C)